watch dirs for changes

This commit is contained in:
rabite 2019-02-26 22:31:33 +01:00
parent bdbe8e07e3
commit fd67621dee
7 changed files with 188 additions and 8 deletions

View File

@ -19,6 +19,7 @@ chrono = "0.4"
libc = "*"
failure = "0.1.5"
failure_derive = "0.1.1"
notify = "4.0.9"
#[profile.release]
#debug = true

View File

@ -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 {

View File

@ -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
}
}
}

View File

@ -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,

View File

@ -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();
}

View File

@ -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;

View File

@ -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() })
}