summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorrabite <rabite@posteo.de>2019-02-26 22:31:33 +0100
committerrabite <rabite@posteo.de>2019-02-26 22:31:33 +0100
commitfd67621dee25990cca67766865ec3e991b5bf11e (patch)
tree8127263133383418f72471d5788552b0ad370bf0 /src
parentbdbe8e07e35de1534718254d3de120de05858943 (diff)
watch dirs for changes
Diffstat (limited to 'src')
-rw-r--r--src/fail.rs6
-rw-r--r--src/file_browser.rs123
-rw-r--r--src/files.rs55
-rw-r--r--src/listview.rs3
-rw-r--r--src/main.rs1
-rw-r--r--src/preview.rs7
6 files changed, 187 insertions, 8 deletions
diff --git a/src/fail.rs b/src/fail.rs
index 9e3c5da..3e85e11 100644
--- a/src/fail.rs
+++ b/src/fail.rs
@@ -1,6 +1,8 @@
use failure;
use failure::Fail;
+use std::path::PathBuf;
+
pub type HResult<T> = Result<T, HError>;
#[derive(Fail, Debug)]
@@ -28,7 +30,9 @@ pub enum HError {
#[fail(display = "Not ready yet!")]
WillBeNotReady,
#[fail(display = "No widget found")]
- NoWidgetError
+ NoWidgetError,
+ #[fail(display = "Path: {:?} not in this directory: {:?}", path, dir)]
+ WrongDirectoryError{ path: PathBuf, dir: PathBuf }
}
impl From<std::io::Error> for HError {
diff --git a/src/file_browser.rs b/src/file_browser.rs
index 65aca45..31e15da 100644
--- a/src/file_browser.rs
+++ b/src/file_browser.rs
@@ -1,8 +1,12 @@
use termion::event::Key;
+use notify::{INotifyWatcher, Watcher, DebouncedEvent, RecursiveMode};
use std::error::Error;
use std::io::Write;
use std::sync::{Arc, Mutex};
+use std::sync::mpsc::{channel, Receiver};
+use std::time::Duration;
+use std::path::PathBuf;
use crate::coordinates::{Coordinates};
use crate::files::{File, Files};
@@ -11,12 +15,17 @@ use crate::miller_columns::MillerColumns;
use crate::widget::Widget;
use crate::tabview::{TabView, Tabbable};
use crate::preview::WillBeWidget;
-use crate::fail::HResult;
+use crate::fail::{HResult, HError};
+use crate::window::{Events, send_event};
+
+
-#[derive(PartialEq)]
pub struct FileBrowser {
pub columns: MillerColumns<WillBeWidget<ListView<Files>>>,
- pub cwd: File
+ pub cwd: File,
+ watcher: INotifyWatcher,
+ watches: Vec<PathBuf>,
+ dir_events: Arc<Mutex<Vec<DebouncedEvent>>>
}
impl Tabbable for TabView<FileBrowser> {
@@ -67,6 +76,23 @@ impl Tabbable for TabView<FileBrowser> {
}
}
+
+
+
+
+fn watch_dir(rx: Receiver<DebouncedEvent>, dir_events: Arc<Mutex<Vec<DebouncedEvent>>>) {
+ std::thread::spawn(move || {
+ for event in rx.iter() {
+ dir_events.lock().unwrap().push(event);
+ send_event(Events::WidgetReady).unwrap();
+ }
+ });
+}
+
+
+
+
+
impl FileBrowser {
pub fn new() -> Result<FileBrowser, Box<Error>> {
let cwd = std::env::current_dir().unwrap();
@@ -93,9 +119,17 @@ impl FileBrowser {
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());
Ok(FileBrowser { columns: miller,
- cwd: cwd })
+ cwd: cwd,
+ watcher: watcher,
+ watches: vec![],
+ dir_events: dir_events })
}
pub fn enter_dir(&mut self) -> HResult<()> {
@@ -188,6 +222,70 @@ impl FileBrowser {
Ok(())
}
+ pub fn left_dir(&self) -> HResult<File> {
+ let widget = self.columns.get_left_widget()?.widget()?;
+ let dir = (*widget.lock()?).as_ref()?.content.directory.clone();
+ Ok(dir)
+ }
+
+ fn update_watches(&mut self) -> HResult<()> {
+ let watched_dirs = self.watches.clone();
+ let cwd = self.cwd()?;
+ 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).unwrap();
+ self.watches.remove_item(&watched_dir);
+ }
+ }
+ if !watched_dirs.contains(&cwd.path) {
+ self.watcher.watch(&cwd.path, RecursiveMode::NonRecursive).unwrap();
+ self.watches.push(cwd.path);
+ }
+ if !watched_dirs.contains(&left_dir.path) {
+ self.watcher.watch(&left_dir.path, RecursiveMode::NonRecursive).unwrap();
+ self.watches.push(left_dir.path);
+ }
+ if let Some(preview_dir) = preview_dir {
+ if !watched_dirs.contains(&preview_dir) {
+ self.watcher.watch(&preview_dir, RecursiveMode::NonRecursive).unwrap();
+ self.watches.push(preview_dir);
+ }
+ }
+ Ok(())
+ }
+
+ fn handle_dir_events(&mut self) -> HResult<()> {
+ let mut dir_events = self.dir_events.lock()?;
+ for event in dir_events.iter() {
+ let main_widget = self.columns.get_main_widget()?.widget()?;
+ let main_files = &mut (*main_widget.lock()?);
+ let main_files = &mut main_files.as_mut()?.content;
+ let main_result = main_files.handle_event(event);
+
+ let left_widget = self.columns.get_left_widget()?.widget()?;
+ let left_files = &mut (*left_widget.lock()?);
+ let left_files = &mut left_files.as_mut()?.content;
+ let left_result = left_files.handle_event(event);
+
+ match main_result {
+ Err(HError::WrongDirectoryError { .. }) => {
+ match left_result {
+ Err(HError::WrongDirectoryError { .. }) => {
+ let preview = &mut self.columns.preview;
+ preview.reload();
+ }, _ => {}
+ }
+ }, _ => {}
+ }
+ }
+ dir_events.clear();
+ Ok(())
+ }
+
pub fn selected_file(&self) -> HResult<File> {
let widget = self.main_widget()?;
let file = widget.lock()?.as_ref()?.selected_file().clone();
@@ -352,11 +450,13 @@ impl Widget for FileBrowser {
crate::term::goto_xy(count_xpos, count_ypos), file_count)
}
fn refresh(&mut self) {
- self.update_preview().ok();
+ self.handle_dir_events().ok();
+ self.columns.refresh();
self.fix_left().ok();
self.fix_selection().ok();
self.set_cwd().ok();
- self.columns.refresh();
+ self.update_watches().ok();
+ self.update_preview().ok();
}
fn get_drawlist(&self) -> String {
@@ -378,3 +478,14 @@ impl Widget for FileBrowser {
self.update_preview().ok();
}
}
+
+impl PartialEq for FileBrowser {
+ fn eq(&self, other: &FileBrowser) -> bool {
+ if self.columns == other.columns && self.cwd == other.cwd {
+ true
+ } else {
+ false
+ }
+ }
+}
+
diff --git a/src/files.rs b/src/files.rs
index f546a00..44863e6 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -8,8 +8,9 @@ use mime_detective;
use users;
use chrono::TimeZone;
use failure::Error;
+use notify::{INotifyWatcher, Watcher, DebouncedEvent, RecursiveMode};
-use crate::fail::HResult;
+use crate::fail::{HResult, HError};
use std::sync::{Arc, Mutex};
@@ -189,6 +190,53 @@ impl Files {
self.files = files;
}
+ pub fn handle_event(&mut self, event: &DebouncedEvent) -> HResult<()> {
+ match event {
+ DebouncedEvent::Create(path) => {
+ self.path_in_here(&path)?;
+ let file = File::new_from_path(&path)?;
+ self.files.push(file);
+ },
+ DebouncedEvent::Write(path) | DebouncedEvent::Chmod(path) => {
+ self.path_in_here(&path)?;
+ let file = self.find_file_with_path(&path)?;
+ file.reload_meta();
+ },
+ DebouncedEvent::Remove(path) => {
+ self.path_in_here(&path)?;
+ let file = self.find_file_with_path(&path)?.clone();
+ self.files.remove_item(&file);
+ },
+ DebouncedEvent::Rename(old_path, new_path) => {
+ self.path_in_here(&new_path)?;
+ let mut file = self.find_file_with_path(&old_path)?;
+ file.name = new_path.file_name()?.to_string_lossy().to_string();
+ file.path = new_path.into();
+ },
+ DebouncedEvent::Error(err, path) => {
+ dbg!(err);
+ dbg!(path);
+ },
+ _ => {},
+ }
+ Ok(())
+ }
+
+ pub fn path_in_here(&self, path: &Path) -> HResult<bool> {
+ let dir = self.directory.path();
+ let path = if path.is_dir() { path } else { path.parent().unwrap() };
+ if dir == path {
+ Ok(true)
+ } else {
+ Err(HError::WrongDirectoryError{path: path.into(),
+ dir: dir})
+ }
+ }
+
+ pub fn find_file_with_path(&mut self, path: &Path) -> Option<&mut File> {
+ self.files.iter_mut().find(|file| file.path == path)
+ }
+
pub fn meta_all(&mut self) {
let len = self.files.len();
self.meta_upto(len);
@@ -306,6 +354,11 @@ impl File {
Ok(())
}
+ pub fn reload_meta(&mut self) -> HResult<()> {
+ self.meta = None;
+ self.get_meta()
+ }
+
fn get_color(&self, meta: &std::fs::Metadata) -> Option<lscolors::Color> {
match COLORS.style_for_path_with_metadata(&self.path, Some(&meta)) {
Some(style) => style.clone().foreground,
diff --git a/src/listview.rs b/src/listview.rs
index 70557f1..19e806a 100644
--- a/src/listview.rs
+++ b/src/listview.rs
@@ -379,6 +379,9 @@ impl<T> Widget for ListView<T> where ListView<T>: Listable {
fn refresh(&mut self) {
self.on_refresh();
self.lines = self.len();
+ if self.selection >= self.lines {
+ self.selection -= 1;
+ }
self.buffer = self.render();
}
diff --git a/src/main.rs b/src/main.rs
index 1d92fba..ea5f92c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -16,6 +16,7 @@ extern crate chrono;
extern crate mime_detective;
extern crate rayon;
extern crate libc;
+extern crate notify;
use termion::input::MouseTerminal;
use termion::raw::IntoRawMode;
diff --git a/src/preview.rs b/src/preview.rs
index 0e7ab82..273c13a 100644
--- a/src/preview.rs
+++ b/src/preview.rs
@@ -265,6 +265,13 @@ impl Previewer {
}))));
}
+ pub fn reload(&mut self) {
+ if let Some(file) = self.file.clone() {
+ self.file = None;
+ self.set_file(&file);
+ }
+ }
+
fn preview_failed(file: &File) -> HResult<WidgetO> {
Err(HError::PreviewFailed { file: file.name.clone() })
}