diff options
author | Christoph Rüßler <christoph.ruessler@mailbox.org> | 2023-04-29 17:03:43 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-29 17:03:43 +0200 |
commit | 370aff5fcc316bf381c650942065c06ffb5eb76e (patch) | |
tree | 52c87b06cf65231a05dddc9039bb697d9ac8e3f5 /src | |
parent | 836e03c01c9cadc07be0ce0ab7df097688ac34e1 (diff) |
Default to tick-based updates (#1657)
* Default to tick-based updates
This commit reintroduces code that was previously removed in favor of a
notify-based update trigger. It turned out that notify-based updates can
cause issues in larger repositories, so tick-based updates seemed like a
safer default.
https://github.com/extrawurst/gitui/issues/1444
https://github.com/extrawurst/gitui/pull/1310
* Add FAQ entry for --watcher
* Remove --poll
Diffstat (limited to 'src')
-rw-r--r-- | src/args.rs | 14 | ||||
-rw-r--r-- | src/main.rs | 48 | ||||
-rw-r--r-- | src/watcher.rs | 47 |
3 files changed, 57 insertions, 52 deletions
diff --git a/src/args.rs b/src/args.rs index 9689164a..7dd3853c 100644 --- a/src/args.rs +++ b/src/args.rs @@ -15,7 +15,7 @@ use std::{ pub struct CliArgs { pub theme: PathBuf, pub repo_path: RepoPath, - pub poll_watcher: bool, + pub notify_watcher: bool, } pub fn process_cmdline() -> Result<CliArgs> { @@ -54,13 +54,13 @@ pub fn process_cmdline() -> Result<CliArgs> { get_app_config_path()?.join("theme.ron") }; - let arg_poll: bool = - *arg_matches.get_one("poll").unwrap_or(&false); + let notify_watcher: bool = + *arg_matches.get_one("watcher").unwrap_or(&false); Ok(CliArgs { theme, - poll_watcher: arg_poll, repo_path, + notify_watcher, }) } @@ -96,9 +96,9 @@ fn app() -> ClapApp { .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") + Arg::new("watcher") + .help("Use notify-based file system watcher instead of tick-based update. This is more performant, but can cause issues on some platforms. See https://github.com/extrawurst/gitui/blob/master/FAQ.md#watcher for details.") + .long("watcher") .action(clap::ArgAction::SetTrue), ) .arg( diff --git a/src/main.rs b/src/main.rs index 26e4b5aa..b1c0ed1f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,7 +56,7 @@ use asyncgit::{ AsyncGitNotification, }; use backtrace::Backtrace; -use crossbeam_channel::{tick, unbounded, Receiver, Select}; +use crossbeam_channel::{never, tick, unbounded, Receiver, Select}; use crossterm::{ terminal::{ disable_raw_mode, enable_raw_mode, EnterAlternateScreen, @@ -83,11 +83,13 @@ use std::{ use ui::style::Theme; use watcher::RepoWatcher; +static TICK_INTERVAL: Duration = Duration::from_secs(5); static SPINNER_INTERVAL: Duration = Duration::from_millis(80); /// #[derive(Clone)] pub enum QueueEvent { + Tick, Notify, SpinnerUpdate, AsyncEvent(AsyncNotification), @@ -114,6 +116,12 @@ pub enum AsyncNotification { Git(AsyncGitNotification), } +#[derive(Clone, Copy, PartialEq)] +enum Updater { + Ticker, + NotifyWatcher, +} + fn main() -> Result<()> { let app_start = Instant::now(); @@ -145,6 +153,12 @@ fn main() -> Result<()> { let mut repo_path = cliargs.repo_path; let input = Input::new(); + let updater = if cliargs.notify_watcher { + Updater::NotifyWatcher + } else { + Updater::Ticker + }; + loop { let quit_state = run_app( app_start, @@ -152,7 +166,7 @@ fn main() -> Result<()> { theme, key_config.clone(), &input, - cliargs.poll_watcher, + updater, &mut terminal, )?; @@ -173,18 +187,24 @@ fn run_app( theme: Theme, key_config: KeyConfig, input: &Input, - poll_watcher: bool, + updater: Updater, terminal: &mut Terminal<CrosstermBackend<io::Stdout>>, ) -> Result<QuitState, anyhow::Error> { 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(), - poll_watcher, - ); - let rx_watcher = watcher.receiver(); + + let (rx_ticker, rx_watcher) = match updater { + Updater::NotifyWatcher => { + let repo_watcher = + RepoWatcher::new(repo_work_dir(&repo)?.as_str()); + + (never(), repo_watcher.receiver()) + } + Updater::Ticker => (tick(TICK_INTERVAL), never()), + }; + let spinner_ticker = tick(SPINNER_INTERVAL); let mut app = App::new( @@ -210,6 +230,7 @@ fn run_app( &rx_input, &rx_git, &rx_app, + &rx_ticker, &rx_watcher, &spinner_ticker, )? @@ -235,7 +256,9 @@ fn run_app( } app.event(ev)?; } - QueueEvent::Notify => app.update()?, + QueueEvent::Tick | QueueEvent::Notify => { + app.update()?; + } QueueEvent::AsyncEvent(ev) => { if !matches!( ev, @@ -309,6 +332,7 @@ fn select_event( rx_input: &Receiver<InputEvent>, rx_git: &Receiver<AsyncGitNotification>, rx_app: &Receiver<AsyncAppNotification>, + rx_ticker: &Receiver<Instant>, rx_notify: &Receiver<()>, rx_spinner: &Receiver<Instant>, ) -> Result<QueueEvent> { @@ -317,6 +341,7 @@ fn select_event( sel.recv(rx_input); sel.recv(rx_git); sel.recv(rx_app); + sel.recv(rx_ticker); sel.recv(rx_notify); sel.recv(rx_spinner); @@ -331,8 +356,9 @@ fn select_event( 2 => oper.recv(rx_app).map(|e| { QueueEvent::AsyncEvent(AsyncNotification::App(e)) }), - 3 => oper.recv(rx_notify).map(|_| QueueEvent::Notify), - 4 => oper.recv(rx_spinner).map(|_| QueueEvent::SpinnerUpdate), + 3 => oper.recv(rx_ticker).map(|_| QueueEvent::Notify), + 4 => oper.recv(rx_notify).map(|_| QueueEvent::Notify), + 5 => oper.recv(rx_spinner).map(|_| QueueEvent::SpinnerUpdate), _ => bail!("unknown select source"), }?; diff --git a/src/watcher.rs b/src/watcher.rs index ee0c3aa0..42fe8dcf 100644 --- a/src/watcher.rs +++ b/src/watcher.rs @@ -1,12 +1,7 @@ use anyhow::Result; use crossbeam_channel::{unbounded, Sender}; -use notify::{ - Config, Error, PollWatcher, RecommendedWatcher, RecursiveMode, - Watcher, -}; -use notify_debouncer_mini::{ - new_debouncer, new_debouncer_opt, DebouncedEvent, -}; +use notify::{Error, RecommendedWatcher, RecursiveMode, Watcher}; +use notify_debouncer_mini::{new_debouncer, DebouncedEvent}; use scopetime::scope_time; use std::{path::Path, thread, time::Duration}; @@ -15,9 +10,9 @@ pub struct RepoWatcher { } impl RepoWatcher { - pub fn new(workdir: &str, poll: bool) -> Self { + pub fn new(workdir: &str) -> Self { log::trace!( - "poll watcher: {poll} recommended: {:?}", + "recommended watcher: {:?}", RecommendedWatcher::kind() ); @@ -27,7 +22,7 @@ impl RepoWatcher { thread::spawn(move || { let timeout = Duration::from_secs(2); - create_watcher(poll, timeout, tx, &workdir); + create_watcher(timeout, tx, &workdir); }); let (out_tx, out_rx) = unbounded(); @@ -72,7 +67,6 @@ impl RepoWatcher { } fn create_watcher( - poll: bool, timeout: Duration, tx: std::sync::mpsc::Sender< Result<Vec<DebouncedEvent>, Vec<Error>>, @@ -81,27 +75,12 @@ fn create_watcher( ) { 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); - }; + 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); } |