diff options
author | rabite <rabite@posteo.de> | 2019-04-06 17:22:28 +0200 |
---|---|---|
committer | rabite <rabite@posteo.de> | 2019-04-06 17:22:28 +0200 |
commit | 7f70fa29046e09be414b52ba572eb9675979cd46 (patch) | |
tree | 49c753d75c71c43b6ca2d3093e5b361822e7361a /src | |
parent | cab0de7a05fd446e68fc10d37253d67400c33417 (diff) | |
parent | 6098ad301595c0c808634fe03e2d194d3fc65c3c (diff) |
Merge branch 'master' into evil
Diffstat (limited to 'src')
-rw-r--r-- | src/bookmarks.rs | 49 | ||||
-rw-r--r-- | src/config.rs | 58 | ||||
-rw-r--r-- | src/dirty.rs | 63 | ||||
-rw-r--r-- | src/fail.rs | 130 | ||||
-rw-r--r-- | src/file_browser.rs | 708 | ||||
-rw-r--r-- | src/files.rs | 490 | ||||
-rw-r--r-- | src/foldview.rs | 2 | ||||
-rw-r--r-- | src/fscache.rs | 366 | ||||
-rw-r--r-- | src/hbox.rs | 13 | ||||
-rw-r--r-- | src/listview.rs | 118 | ||||
-rw-r--r-- | src/main.rs | 16 | ||||
-rw-r--r-- | src/minibuffer.rs | 2 | ||||
-rw-r--r-- | src/paths.rs | 6 | ||||
-rw-r--r-- | src/preview.rs | 773 | ||||
-rw-r--r-- | src/proclist.rs | 62 | ||||
-rw-r--r-- | src/stats.rs | 84 | ||||
-rw-r--r-- | src/tabview.rs | 13 | ||||
-rw-r--r-- | src/term.rs | 95 | ||||
-rw-r--r-- | src/textview.rs | 6 | ||||
-rw-r--r-- | src/widget.rs | 121 |
20 files changed, 2282 insertions, 893 deletions
diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 379565e..326be7c 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -5,7 +5,6 @@ use std::collections::HashMap; use crate::fail::{HResult, HError, ErrorLog}; use crate::widget::{Widget, WidgetCore}; use crate::coordinates::Coordinates; -use crate::files::{Files, File}; use crate::term; #[derive(PartialEq, Eq, Clone, Debug)] @@ -30,27 +29,41 @@ impl Bookmarks { } pub fn load(&mut self) -> HResult<()> { let bm_file = crate::paths::bookmark_path()?; - let bm_content = std::fs::read_to_string(bm_file)?; - - let keys = bm_content.lines().step_by(2).map(|k| k); - let paths = bm_content.lines().skip(1).step_by(2).map(|p| p); + if !bm_file.exists() { + self.import().log(); + } - let mapping = keys.zip(paths).fold(HashMap::new(), |mut mapping, (key, path)| { - if let Some(key) = key.chars().next() { - let path = path.to_string(); - mapping.insert(key, path); + let bm_content = std::fs::read_to_string(bm_file)?; + let mapping = bm_content.lines() + .fold(HashMap::new(), |mut bm, line| { + let parts = line.splitn(2, ":").collect::<Vec<&str>>(); + if parts.len() == 2 { + if let Some(key) = parts[0].chars().next() { + let path = parts[1].to_string(); + bm.insert(key, path); + } } - mapping + bm }); self.mapping = mapping; Ok(()) } + pub fn import(&self) -> HResult<()> { + let mut ranger_bm_path = crate::paths::ranger_path()?; + ranger_bm_path.push("bookmarks"); + + if ranger_bm_path.exists() { + let bm_file = crate::paths::bookmark_path()?; + std::fs::copy(ranger_bm_path, bm_file)?; + } + Ok(()) + } pub fn save(&self) -> HResult<()> { let bm_file = crate::paths::bookmark_path()?; let bookmarks = self.mapping.iter().map(|(key, path)| { - format!("{}\n{}\n", key, path) + format!("{}:{}\n", key, path) }).collect::<String>(); std::fs::write(bm_file, bookmarks)?; @@ -86,6 +99,7 @@ impl BMPopup { Ok(_) => {}, Err(HError::PopupFinnished) => {}, err @ Err(HError::TerminalResizedError) => err?, + err @ Err(HError::WidgetResizedError) => err?, err @ Err(_) => err?, } self.clear()?; @@ -104,6 +118,10 @@ impl BMPopup { Ok(()) } + fn resize(&mut self) -> HResult<()> { + HError::terminal_resized()? + } + pub fn render_line(&self, n: u16, key: &char, path: &str) -> String { let xsize = term::xsize(); let padding = xsize - 4; @@ -134,13 +152,13 @@ impl Widget for BMPopup { HError::terminal_resized() } - fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> { - let (xsize, ysize) = coordinates.size_u(); + fn set_coordinates(&mut self, _: &Coordinates) -> HResult<()> { + let (xsize, ysize) = crate::term::size()?; let len = self.bookmarks.mapping.len(); let ysize = ysize.saturating_sub( len + 1 ); self.core.coordinates.set_size_u(xsize.saturating_sub(1), len); - self.core.coordinates.set_position_u(1, ysize+2); + self.core.coordinates.set_position_u(1, ysize); Ok(()) } @@ -176,6 +194,7 @@ impl Widget for BMPopup { let path = self.bookmark_path.take()?; self.bookmarks.add(key, &path)?; self.add_mode = false; + self.bookmarks.save().log(); return HError::popup_finnished(); } if let Ok(path) = self.bookmarks.get(key) { @@ -185,6 +204,8 @@ impl Widget for BMPopup { } Key::Alt(key) => { self.bookmarks.mapping.remove(&key); + self.bookmarks.save().log(); + return HError::widget_resized(); } _ => {} } diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..4801286 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,58 @@ +use crate::paths; +use crate::fail::{HError, HResult, ErrorLog}; + +#[derive(Debug, Clone)] +pub struct Config { + pub animation: bool, + pub show_hidden: bool, +} + + +impl Config { + pub fn new() -> Config { + Config { + animation: true, + show_hidden: false + } + } + + pub fn load() -> HResult<Config> { + let config_path = paths::config_path()?; + + if !config_path.exists() { + return Ok(Config::new()); + } + + let config_string = std::fs::read_to_string(config_path)?; + + let config = config_string.lines().fold(Config::new(), |mut config, line| { + match Config::prep_line(line) { + Ok(("animation", "on")) => { config.animation = true; }, + Ok(("animation", "off")) => { config.animation = false; }, + Ok(("show_hidden", "on")) => { config.show_hidden = true; }, + Ok(("show_hidden", "off")) => { config.show_hidden = false; }, + _ => { HError::config_error::<Config>(line.to_string()).log(); } + } + config + }); + Ok(config) + } + + fn prep_line<'a>(line: &'a str) -> HResult<(&'a str, &'a str)> { + let setting = line.split("=").collect::<Vec<&str>>(); + if setting.len() == 2 { + Ok((setting[0], setting[1])) + } else { + HError::config_error(line.to_string()) + } + + } + + pub fn animate(&self) -> bool { + self.animation + } + + pub fn show_hidden(&self) -> bool { + self.show_hidden + } +} diff --git a/src/dirty.rs b/src/dirty.rs index fe6e4d1..14c6245 100644 --- a/src/dirty.rs +++ b/src/dirty.rs @@ -1,22 +1,38 @@ +use std::sync::{Arc, RwLock}; +use std::hash::{Hash, Hasher}; + #[derive(PartialEq, Eq, Hash, Clone, Debug)] pub struct DirtyBit(bool); -pub trait Dirtyable { - fn get_bit(&self) -> &DirtyBit; - fn get_bit_mut(&mut self) -> &mut DirtyBit; +#[derive(Debug)] +pub struct AsyncDirtyBit(pub Arc<RwLock<DirtyBit>>); - fn is_dirty(&self) -> bool { - self.get_bit().0 +impl PartialEq for AsyncDirtyBit { + fn eq(&self, other: &AsyncDirtyBit) -> bool { + *self.0.read().unwrap() == *other.0.read().unwrap() } +} - fn set_dirty(&mut self) { - self.get_bit_mut().0 = true; +impl Eq for AsyncDirtyBit {} + +impl Hash for AsyncDirtyBit { + fn hash<H: Hasher>(&self, state: &mut H) { + self.0.read().unwrap().hash(state) } - fn set_clean(&mut self) { - self.get_bit_mut().0 = false; +} + +impl Clone for AsyncDirtyBit { + fn clone(&self) -> Self { + AsyncDirtyBit(self.0.clone()) } } +pub trait Dirtyable { + fn is_dirty(&self) -> bool; + fn set_dirty(&mut self); + fn set_clean(&mut self); +} + impl DirtyBit { pub fn new() -> DirtyBit { @@ -24,12 +40,33 @@ impl DirtyBit { } } +impl AsyncDirtyBit { + pub fn new() -> AsyncDirtyBit { + AsyncDirtyBit(Arc::new(RwLock::new(DirtyBit::new()))) + } +} + impl Dirtyable for DirtyBit { - fn get_bit(&self) -> &DirtyBit { - self + fn is_dirty(&self) -> bool { + self.0 + } + fn set_dirty(&mut self) { + self.0 = true; } - fn get_bit_mut(&mut self) -> &mut DirtyBit { - self + fn set_clean(&mut self) { + self.0 = false; + } +} + +impl Dirtyable for AsyncDirtyBit { + fn is_dirty(&self) -> bool { + self.0.read().unwrap().is_dirty() + } + fn set_dirty(&mut self) { + self.0.write().unwrap().set_dirty(); + } + fn set_clean(&mut self) { + self.0.write().unwrap().set_clean(); } } diff --git a/src/fail.rs b/src/fail.rs index 6cf4afe..15a8b2c 100644 --- a/src/fail.rs +++ b/src/fail.rs @@ -1,6 +1,6 @@ use failure; use failure::Fail; -use failure::Backtrace; +//use failure::Backtrace; use termion::event::Key; @@ -11,36 +11,46 @@ use crate::foldview::LogEntry; pub type HResult<T> = Result<T, HError>; -#[derive(Fail, Debug)] +#[derive(Fail, Debug, Clone)] pub enum HError { - #[fail(display = "IO error: {} ", error)] - IoError{#[cause] error: std::io::Error, backtrace: Backtrace}, + #[fail(display = "IO error: {} ", _0)] + IoError(String), #[fail(display = "Mutex failed")] - MutexError(Backtrace), + MutexError, #[fail(display = "Can't lock!")] - TryLockError(Backtrace), + TryLockError, #[fail(display = "Channel failed: {}", error)] ChannelTryRecvError{#[cause] error: std::sync::mpsc::TryRecvError}, #[fail(display = "Channel failed: {}", error)] ChannelRecvError{#[cause] error: std::sync::mpsc::RecvError}, #[fail(display = "Channel failed")] - ChannelSendError(Backtrace), + ChannelSendError, #[fail(display = "Previewer failed on file: {}", file)] - PreviewFailed{file: String, backtrace: Backtrace}, + PreviewFailed{file: String}, #[fail(display = "StalePreviewer for file: {}", file)] StalePreviewError{file: String}, #[fail(display = "Accessed stale value")] - StaleError(Backtrace), - #[fail(display = "Failed: {}", error)] - Error{#[cause] error: failure::Error , backtrace: Backtrace}, + StaleError, + #[fail(display = "Failed: {}", _0)] + Error(String), #[fail(display = "Was None!")] - NoneError(Backtrace), + NoneError, #[fail(display = "Not ready yet!")] - WillBeNotReady(Backtrace), + WillBeNotReady, + #[fail(display = "Not ready yet!")] + AsyncNotReadyError, + #[fail(display = "Async is stale!")] + AsyncStaleError, + #[fail(display = "Value has already been taken!")] + AsyncAlreadyTakenError, + #[fail(display = "Async has already been started!")] + AsyncAlreadyStartedError, + #[fail(display = "Async Error: {}", _0)] + AsyncError(String), #[fail(display = "No widget found")] - NoWidgetError(Backtrace), + NoWidgetError, #[fail(display = "Path: {:?} not in this directory: {:?}", path, dir)] - WrongDirectoryError{ path: PathBuf, dir: PathBuf , backtrace: Backtrace}, + WrongDirectoryError{ path: PathBuf, dir: PathBuf}, #[fail(display = "Widget finnished")] PopupFinnished, #[fail(display = "No completions found")] @@ -48,7 +58,7 @@ pub enum HError { #[fail(display = "No more history")] NoHistoryError, #[fail(display = "No core for widget")] - NoWidgetCoreError(Backtrace), + NoWidgetCoreError, #[fail(display = "No header for widget")] NoHeaderError, #[fail(display = "You wanted this!")] @@ -56,11 +66,11 @@ pub enum HError { #[fail(display = "HBox ratio mismatch: {} widgets, ratio is {:?}", wnum, ratio)] HBoxWrongRatioError{ wnum: usize, ratio: Vec<usize> }, #[fail(display = "Got wrong widget: {}! Wanted: {}", got, wanted)] - WrongWidgetError{got: String, wanted: String, backtrace: Backtrace}, + WrongWidgetError{got: String, wanted: String}, #[fail(display = "Strip Prefix Error: {}", error)] - StripPrefixError{#[cause] error: std::path::StripPrefixError, backtrace: Backtrace}, - #[fail(display = "INofify failed: {}", error)] - INotifyError{#[cause] error: notify::Error, backtrace: Backtrace}, + StripPrefixError{#[cause] error: std::path::StripPrefixError}, + #[fail(display = "INofify failed: {}", _0)] + INotifyError(String), #[fail(display = "Tags not loaded yet")] TagsNotLoadedYetError, #[fail(display = "Input cancelled!")] @@ -71,12 +81,20 @@ pub enum HError { WidgetUndefinedKeyError{key: Key}, #[fail(display = "Terminal has been resized!")] TerminalResizedError, + #[fail(display = "Widget has been resized!")] + WidgetResizedError, #[fail(display = "{}", _0)] - Log(String) + Log(String), + #[fail(display = "Metadata already processed")] + MetadataProcessedError, + #[fail(display = "No files to take from widget")] + WidgetNoFilesError, + #[fail(display = "Invalid line in settings file: {}", _0)] + ConfigLineError(String), } impl HError { - pub fn log(log: String) -> HResult<()> { + pub fn log<T>(log: String) -> HResult<T> { Err(HError::Log(log)) } pub fn quit() -> HResult<()> { @@ -86,12 +104,12 @@ impl HError { Err(HError::HBoxWrongRatioError{ wnum: wnum, ratio: ratio }) } pub fn no_widget<T>() -> HResult<T> { - Err(HError::NoWidgetError(Backtrace::new())) + Err(HError::NoWidgetError) } pub fn wrong_widget<T>(got: &str, wanted: &str) -> HResult<T> { Err(HError::WrongWidgetError{ got: got.to_string(), - wanted: wanted.to_string(), - backtrace: Backtrace::new()}) + wanted: wanted.to_string() }) + } pub fn popup_finnished<T>() -> HResult<T> { Err(HError::PopupFinnished) @@ -110,21 +128,53 @@ impl HError { } pub fn wrong_directory<T>(path: PathBuf, dir: PathBuf) -> HResult<T> { Err(HError::WrongDirectoryError{ path: path, - dir: dir, - backtrace: Backtrace::new() }) + dir: dir }) + } pub fn preview_failed<T>(file: &crate::files::File) -> HResult<T> { let name = file.name.clone(); - Err(HError::PreviewFailed{ file: name, - backtrace: Backtrace::new() }) + Err(HError::PreviewFailed{ file: name }) + } pub fn terminal_resized<T>() -> HResult<T> { Err(HError::TerminalResizedError) } + pub fn widget_resized<T>() -> HResult<T> { + Err(HError::WidgetResizedError) + } + pub fn stale<T>() -> HResult<T> { - Err(HError::StaleError(Backtrace::new())) + Err(HError::StaleError) + } + + pub fn config_error<T>(line: String) -> HResult<T> { + Err(HError::ConfigLineError(line)) + } + + pub fn async_not_ready<T>() -> HResult<T> { + Err(HError::AsyncNotReadyError) + } + + pub fn async_taken<T>() -> HResult<T> { + Err(HError::AsyncAlreadyTakenError) + } + + pub fn async_error<T>(error: &HError) -> HResult<T> { + Err(HError::AsyncError(format!("{}", error))) + } + + pub fn async_started<T>() -> HResult<T> { + Err(HError::AsyncAlreadyStartedError) + } + + pub fn metadata_processed<T>() -> HResult<T> { + Err(HError::MetadataProcessedError) + } + + pub fn no_files<T>() -> HResult<T> { + Err(HError::WidgetNoFilesError) } } @@ -177,7 +227,7 @@ impl<T> ErrorLog for HResult<T> { impl From<std::io::Error> for HError { fn from(error: std::io::Error) -> Self { // dbg!(&error); - let err = HError::IoError { error: error, backtrace: Backtrace::new() }; + let err = HError::IoError(format!("{}", error)); put_log(&err).ok(); err } @@ -186,7 +236,7 @@ impl From<std::io::Error> for HError { impl From<failure::Error> for HError { fn from(error: failure::Error) -> Self { // dbg!(&error); - let err = HError::Error { error: error, backtrace: Backtrace::new() }; + let err = HError::Error(format!("{}", error)); put_log(&err).ok(); err } @@ -213,7 +263,7 @@ impl From<std::sync::mpsc::RecvError> for HError { impl<T> From<std::sync::mpsc::SendError<T>> for HError { fn from(error: std::sync::mpsc::SendError<T>) -> Self { dbg!(&error); - let err = HError::ChannelSendError(Backtrace::new()); + let err = HError::ChannelSendError; put_log(&err).ok(); err } @@ -222,25 +272,25 @@ impl<T> From<std::sync::mpsc::SendError<T>> for HError { impl<T> From<std::sync::PoisonError<T>> for HError { fn from(_: std::sync::PoisonError<T>) -> Self { // dbg!("Poisoned Mutex"); - let err = HError::MutexError(Backtrace::new()); + let err = HError::MutexError; put_log(&err).ok(); err } } impl<T> From<std::sync::TryLockError<T>> for HError { - fn from(error: std::sync::TryLockError<T>) -> Self { + fn from(_error: std::sync::TryLockError<T>) -> Self { // dbg!(&error); - let err = HError::TryLockError(Backtrace::new()); + let err = HError::TryLockError; put_log(&err).ok(); err } } impl From<std::option::NoneError> for HError { - fn from(error: std::option::NoneError) -> Self { + fn from(_error: std::option::NoneError) -> Self { //dbg!(&error); - let err = HError::NoneError(Backtrace::new()); + let err = HError::NoneError; //put_log(&err).ok(); err } @@ -249,7 +299,7 @@ impl From<std::option::NoneError> for HError { impl From<std::path::StripPrefixError> for HError { fn from(error: std::path::StripPrefixError) -> Self { // dbg!(&error); - let err = HError::StripPrefixError{error: error, backtrace: Backtrace::new() }; + let err = HError::StripPrefixError{error: error }; put_log(&err).ok(); err } @@ -258,7 +308,7 @@ impl From<std::path::StripPrefixError> for HError { impl From<notify::Error> for HError { fn from(error: notify::Error) -> Self { // dbg!(&error); - let err = HError::INotifyError{error: error, backtrace: Backtrace::new() }; + let err = HError::INotifyError(format!("{}", error)); put_log(&err).ok(); err } diff --git a/src/file_browser.rs b/src/file_browser.rs index d083896..9458da7 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -1,21 +1,19 @@ 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::sync::{Arc, Mutex, RwLock}; use std::path::PathBuf; -use std::collections::HashMap; -use std::ffi::{OsString, OsStr}; +use std::ffi::OsString; +use std::collections::HashSet; -use crate::files::{File, Files, PathBufExt, OsStrTools}; +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, WillBeWidget}; +use crate::preview::{Previewer, AsyncWidget}; +use crate::textview::TextView; use crate::fail::{HResult, HError, ErrorLog}; use crate::widget::{Events, WidgetCore}; use crate::proclist::ProcView; @@ -24,42 +22,50 @@ use crate::term; use crate::term::ScreenExt; use crate::foldview::LogView; use crate::coordinates::Coordinates; +use crate::dirty::Dirtyable; +use crate::stats::{FsStat, FsExt}; #[derive(PartialEq)] pub enum FileBrowserWidgets { - FileList(WillBeWidget<ListView<Files>>), + FileList(AsyncWidget<ListView<Files>>), Previewer(Previewer), + Blank(AsyncWidget<TextView>), } impl Widget for FileBrowserWidgets { fn get_core(&self) -> HResult<&WidgetCore> { match self { FileBrowserWidgets::FileList(widget) => widget.get_core(), - FileBrowserWidgets::Previewer(widget) => widget.get_core() + FileBrowserWidgets::Previewer(widget) => widget.get_core(), + FileBrowserWidgets::Blank(widget) => widget.get_core(), } } fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> { match self { FileBrowserWidgets::FileList(widget) => widget.get_core_mut(), - FileBrowserWidgets::Previewer(widget) => widget.get_core_mut() + FileBrowserWidgets::Previewer(widget) => widget.get_core_mut(), + FileBrowserWidgets::Blank(widget) => widget.get_core_mut(), } } fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> { match self { FileBrowserWidgets::FileList(widget) => widget.set_coordinates(coordinates), FileBrowserWidgets::Previewer(widget) => widget.set_coordinates(coordinates), + FileBrowserWidgets::Blank(widget) => widget.set_coordinates(coordinates), } } fn refresh(&mut self) -> HResult<()> { match self { FileBrowserWidgets::FileList(widget) => widget.refresh(), - FileBrowserWidgets::Previewer(widget) => widget.refresh() + FileBrowserWidgets::Previewer(widget) => widget.refresh(), + FileBrowserWidgets::Blank(widget) => widget.refresh(), } } fn get_drawlist(&self) -> HResult<String> { match self { FileBrowserWidgets::FileList(widget) => widget.get_drawlist(), - FileBrowserWidgets::Previewer(widget) => widget.get_drawlist() + FileBrowserWidgets::Previewer(widget) => widget.get_drawlist(), + FileBrowserWidgets::Blank(widget) => widget.get_drawlist(), } } } @@ -68,35 +74,39 @@ 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, + fs_stat: Arc<RwLock<FsStat>> } 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 settings = cur_tab.fs_cache.tab_settings.read()?.clone(); + let cache = cur_tab.fs_cache.new_client(settings).ok(); - 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 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; + tab.fs_stat = cur_tab.fs_stat.clone(); 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 +158,79 @@ 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<_>>(); + + 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() + } + } + let open_dirs = self.widgets + .iter() + .fold(HashSet::new(), |mut dirs, tab| { + tab.left_dir().map(|dir| dirs.insert(dir.clone())).ok(); + dirs.insert(tab.cwd.clone()); + tab.preview_widget() + .map(|preview| preview.get_file().map(|file| { + if file.is_dir() { + dirs.insert(file.clone()); + } + })).ok(); + dirs + }); + self.active_tab_mut_().fs_cache.watch_only(open_dirs).log(); + self.active_tab_mut_().fs_stat.write()?.refresh().log(); + Ok(()) + } -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(); + fn on_config_loaded(&mut self) -> HResult<()> { + // hack: wait a bit for widget readyness... + let duration = std::time::Duration::from_millis(100); + std::thread::sleep(duration); + + let show_hidden = self.config().show_hidden(); + for tab in self.widgets.iter_mut() { + tab.left_widget_mut().map(|w| { + w.content.show_hidden = show_hidden; + w.content.dirty_meta.set_dirty(); + w.refresh().log(); + }).ok(); + + tab.main_widget_mut().map(|w| { + w.content.show_hidden = show_hidden; + w.content.dirty_meta.set_dirty(); + w.content.sort(); + w.refresh().log(); + }).ok(); + + tab.preview_widget_mut().map(|w| w.config_loaded()).ok(); } - }); + Ok(()) + } } + + impl FileBrowser { - pub fn new_cored(core: &WidgetCore) -> HResult<FileBrowser> { + pub fn new(core: &WidgetCore, cache: Option<FsCache>) -> HResult<FileBrowser> { + let startup = cache.is_none(); + 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,25 +251,80 @@ impl FileBrowser { }).last()?; let left_path = main_path.parent().map(|p| p.to_path_buf()); - let main_widget = WillBeWidget::new(&core, Box::new(move |_| { - let mut list = ListView::new(&core_m, - Files::new_from_path(&main_path)?); - list.animate_slide_up().log(); + let cache = fs_cache.clone(); + let main_widget = AsyncWidget::new(&core, Box::new(move |_| { + let name = if main_path.parent().is_none() { + "root".to_string() + } else { + main_path.file_name()? + .to_string_lossy() + .to_string() + }; + let main_dir = File::new(&name, + 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.content.meta_all(); + list.content.dirty_meta.set_dirty(); + list.refresh().log(); + + if startup { + list.animate_slide_up(None).log(); + } + + list.content.meta_all(); Ok(list) })); + let cache = fs_cache.clone(); if let Some(left_path) = left_path { - let left_widget = WillBeWidget::new(&core, Box::new(move |_| { + let left_widget = AsyncWidget::new(&core, Box::new(move |_| { + let name = if left_path.parent().is_none() { + "root".to_string() + } else { + left_path.file_name()? + .to_string_lossy() + .to_string() + }; + let left_dir = File::new(&name, + 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)?); - list.animate_slide_up().log(); + files); + if let Some(file) = selection { + list.select_file(&file); + } + + list.refresh().log(); + + if startup { + list.animate_slide_up(None).log(); + } + Ok(list) })); let left_widget = FileBrowserWidgets::FileList(left_widget); columns.push_widget(left_widget); + } else { + let left_widget = AsyncWidget::new(&core, Box::new(move |_| { + let blank = TextView::new_blank(&core_l); + Ok(blank) + })); + + let left_widget = FileBrowserWidgets::Blank(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::Previewer(previewer)); @@ -217,38 +332,76 @@ impl FileBrowser { columns.refresh().log(); - let cwd = File::new_from_path(&cwd).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 cwd = File::new_from_path(&cwd, None).unwrap(); let proc_view = ProcView::new(&core); |