diff options
author | rabite <rabite@posteo.de> | 2019-02-26 22:31:33 +0100 |
---|---|---|
committer | rabite <rabite@posteo.de> | 2019-02-26 22:31:33 +0100 |
commit | fd67621dee25990cca67766865ec3e991b5bf11e (patch) | |
tree | 8127263133383418f72471d5788552b0ad370bf0 /src | |
parent | bdbe8e07e35de1534718254d3de120de05858943 (diff) |
watch dirs for changes
Diffstat (limited to 'src')
-rw-r--r-- | src/fail.rs | 6 | ||||
-rw-r--r-- | src/file_browser.rs | 123 | ||||
-rw-r--r-- | src/files.rs | 55 | ||||
-rw-r--r-- | src/listview.rs | 3 | ||||
-rw-r--r-- | src/main.rs | 1 | ||||
-rw-r--r-- | src/preview.rs | 7 |
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() }) } |