summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorrabite <rabite@posteo.de>2019-04-06 17:22:28 +0200
committerrabite <rabite@posteo.de>2019-04-06 17:22:28 +0200
commit7f70fa29046e09be414b52ba572eb9675979cd46 (patch)
tree49c753d75c71c43b6ca2d3093e5b361822e7361a /src
parentcab0de7a05fd446e68fc10d37253d67400c33417 (diff)
parent6098ad301595c0c808634fe03e2d194d3fc65c3c (diff)
Merge branch 'master' into evil
Diffstat (limited to 'src')
-rw-r--r--src/bookmarks.rs49
-rw-r--r--src/config.rs58
-rw-r--r--src/dirty.rs63
-rw-r--r--src/fail.rs130
-rw-r--r--src/file_browser.rs708
-rw-r--r--src/files.rs490
-rw-r--r--src/foldview.rs2
-rw-r--r--src/fscache.rs366
-rw-r--r--src/hbox.rs13
-rw-r--r--src/listview.rs118
-rw-r--r--src/main.rs16
-rw-r--r--src/minibuffer.rs2
-rw-r--r--src/paths.rs6
-rw-r--r--src/preview.rs773
-rw-r--r--src/proclist.rs62
-rw-r--r--src/stats.rs84
-rw-r--r--src/tabview.rs13
-rw-r--r--src/term.rs95
-rw-r--r--src/textview.rs6
-rw-r--r--src/widget.rs121
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);