diff --git a/src/args.rs b/src/args.rs index 13621af8..2f58fe07 100644 --- a/src/args.rs +++ b/src/args.rs @@ -2,8 +2,8 @@ use crate::bug_report; use anyhow::{anyhow, Result}; use asyncgit::sync::RepoPath; use clap::{ - crate_authors, crate_description, crate_name, crate_version, Arg, - Command as ClapApp, + crate_authors, crate_description, crate_name, crate_version, + value_parser, Arg, Command as ClapApp, }; use simplelog::{Config, LevelFilter, WriteLogger}; use std::{ @@ -15,6 +15,7 @@ use std::{ pub struct CliArgs { pub theme: PathBuf, pub repo_path: RepoPath, + pub poll_watcher: bool, } pub fn process_cmdline() -> Result { @@ -46,17 +47,20 @@ pub fn process_cmdline() -> Result { .get_one::("theme") .map_or_else(|| PathBuf::from("theme.ron"), PathBuf::from); - if get_app_config_path()?.join(&arg_theme).is_file() { - Ok(CliArgs { - theme: get_app_config_path()?.join(arg_theme), - repo_path, - }) + let theme = if get_app_config_path()?.join(&arg_theme).is_file() { + get_app_config_path()?.join(arg_theme) } else { - Ok(CliArgs { - theme: get_app_config_path()?.join("theme.ron"), - repo_path, - }) - } + get_app_config_path()?.join("theme.ron") + }; + + let arg_poll: bool = + *arg_matches.get_one("poll").unwrap_or(&false); + + Ok(CliArgs { + theme, + poll_watcher: arg_poll, + repo_path, + }) } fn app() -> ClapApp { @@ -90,6 +94,13 @@ fn app() -> ClapApp { .long("logging") .num_args(0), ) + .arg( + Arg::new("poll") + .help("Poll folder for changes instead of using file system events. This can be useful if you run into issues with maximum # of file descriptors") + .long("polling") + .num_args(0) + .value_parser(value_parser!(bool)), + ) .arg( Arg::new("bugreport") .help("Generate a bug report") diff --git a/src/main.rs b/src/main.rs index 6563a372..6a45b857 100644 --- a/src/main.rs +++ b/src/main.rs @@ -150,6 +150,7 @@ fn main() -> Result<()> { theme, key_config.clone(), &input, + cliargs.poll_watcher, &mut terminal, )?; @@ -170,13 +171,17 @@ fn run_app( theme: Theme, key_config: KeyConfig, input: &Input, + poll_watcher: bool, terminal: &mut Terminal>, ) -> Result { let (tx_git, rx_git) = unbounded(); let (tx_app, rx_app) = unbounded(); let rx_input = input.receiver(); - let watcher = RepoWatcher::new(repo_work_dir(&repo)?.as_str())?; + let watcher = RepoWatcher::new( + repo_work_dir(&repo)?.as_str(), + poll_watcher, + ); let rx_watcher = watcher.receiver(); let spinner_ticker = tick(SPINNER_INTERVAL); diff --git a/src/watcher.rs b/src/watcher.rs index b2bfe40f..01f99278 100644 --- a/src/watcher.rs +++ b/src/watcher.rs @@ -1,8 +1,11 @@ use anyhow::Result; use crossbeam_channel::{unbounded, Sender}; -use notify::{Error, RecommendedWatcher, RecursiveMode}; +use notify::{ + Config, Error, PollWatcher, RecommendedWatcher, RecursiveMode, + Watcher, +}; use notify_debouncer_mini::{ - new_debouncer, DebouncedEvent, Debouncer, + new_debouncer, new_debouncer_opt, DebouncedEvent, }; use scopetime::scope_time; use std::{ @@ -11,22 +14,23 @@ use std::{ pub struct RepoWatcher { receiver: crossbeam_channel::Receiver<()>, - #[allow(dead_code)] - debouncer: Debouncer, } impl RepoWatcher { - pub fn new(workdir: &str) -> Result { - scope_time!("RepoWatcher::new"); + pub fn new(workdir: &str, poll: bool) -> Self { + log::trace!( + "poll watcher: {poll} recommended: {:?}", + RecommendedWatcher::kind() + ); let (tx, rx) = std::sync::mpsc::channel(); - let mut debouncer = - new_debouncer(Duration::from_secs(2), None, tx)?; + let workdir = workdir.to_string(); - debouncer - .watcher() - .watch(Path::new(workdir), RecursiveMode::Recursive)?; + thread::spawn(move || { + let timeout = Duration::from_secs(2); + create_watcher(poll, timeout, tx, &workdir); + }); let (out_tx, out_rx) = unbounded(); @@ -37,10 +41,7 @@ impl RepoWatcher { } }); - Ok(Self { - debouncer, - receiver: out_rx, - }) + Self { receiver: out_rx } } /// @@ -71,3 +72,38 @@ impl RepoWatcher { } } } + +fn create_watcher( + poll: bool, + timeout: Duration, + tx: std::sync::mpsc::Sender< + Result, Vec>, + >, + workdir: &str, +) { + scope_time!("create_watcher"); + + if poll { + let config = Config::default() + .with_poll_interval(Duration::from_secs(2)); + let mut bouncer = new_debouncer_opt::<_, PollWatcher>( + timeout, None, tx, config, + ) + .expect("Watch create error"); + bouncer + .watcher() + .watch(Path::new(&workdir), RecursiveMode::Recursive) + .expect("Watch error"); + + std::mem::forget(bouncer); + } else { + let mut bouncer = new_debouncer(timeout, None, tx) + .expect("Watch create error"); + bouncer + .watcher() + .watch(Path::new(&workdir), RecursiveMode::Recursive) + .expect("Watch error"); + + std::mem::forget(bouncer); + }; +}