mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
threaded watcher creation and arg for config
new argument `polling` allows to force watcher to use polling instead of file system events. this should address the issue in #1436 and maybe even #1437
This commit is contained in:
parent
8cdb02349f
commit
bbcadcb5d1
3 changed files with 80 additions and 28 deletions
35
src/args.rs
35
src/args.rs
|
|
@ -2,8 +2,8 @@ use crate::bug_report;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use asyncgit::sync::RepoPath;
|
use asyncgit::sync::RepoPath;
|
||||||
use clap::{
|
use clap::{
|
||||||
crate_authors, crate_description, crate_name, crate_version, Arg,
|
crate_authors, crate_description, crate_name, crate_version,
|
||||||
Command as ClapApp,
|
value_parser, Arg, Command as ClapApp,
|
||||||
};
|
};
|
||||||
use simplelog::{Config, LevelFilter, WriteLogger};
|
use simplelog::{Config, LevelFilter, WriteLogger};
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -15,6 +15,7 @@ use std::{
|
||||||
pub struct CliArgs {
|
pub struct CliArgs {
|
||||||
pub theme: PathBuf,
|
pub theme: PathBuf,
|
||||||
pub repo_path: RepoPath,
|
pub repo_path: RepoPath,
|
||||||
|
pub poll_watcher: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_cmdline() -> Result<CliArgs> {
|
pub fn process_cmdline() -> Result<CliArgs> {
|
||||||
|
|
@ -46,17 +47,20 @@ pub fn process_cmdline() -> Result<CliArgs> {
|
||||||
.get_one::<String>("theme")
|
.get_one::<String>("theme")
|
||||||
.map_or_else(|| PathBuf::from("theme.ron"), PathBuf::from);
|
.map_or_else(|| PathBuf::from("theme.ron"), PathBuf::from);
|
||||||
|
|
||||||
if get_app_config_path()?.join(&arg_theme).is_file() {
|
let theme = if get_app_config_path()?.join(&arg_theme).is_file() {
|
||||||
Ok(CliArgs {
|
get_app_config_path()?.join(arg_theme)
|
||||||
theme: get_app_config_path()?.join(arg_theme),
|
|
||||||
repo_path,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Ok(CliArgs {
|
get_app_config_path()?.join("theme.ron")
|
||||||
theme: get_app_config_path()?.join("theme.ron"),
|
};
|
||||||
repo_path,
|
|
||||||
})
|
let arg_poll: bool =
|
||||||
}
|
*arg_matches.get_one("poll").unwrap_or(&false);
|
||||||
|
|
||||||
|
Ok(CliArgs {
|
||||||
|
theme,
|
||||||
|
poll_watcher: arg_poll,
|
||||||
|
repo_path,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app() -> ClapApp {
|
fn app() -> ClapApp {
|
||||||
|
|
@ -90,6 +94,13 @@ fn app() -> ClapApp {
|
||||||
.long("logging")
|
.long("logging")
|
||||||
.num_args(0),
|
.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(
|
||||||
Arg::new("bugreport")
|
Arg::new("bugreport")
|
||||||
.help("Generate a bug report")
|
.help("Generate a bug report")
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,7 @@ fn main() -> Result<()> {
|
||||||
theme,
|
theme,
|
||||||
key_config.clone(),
|
key_config.clone(),
|
||||||
&input,
|
&input,
|
||||||
|
cliargs.poll_watcher,
|
||||||
&mut terminal,
|
&mut terminal,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
@ -170,13 +171,17 @@ fn run_app(
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
key_config: KeyConfig,
|
key_config: KeyConfig,
|
||||||
input: &Input,
|
input: &Input,
|
||||||
|
poll_watcher: bool,
|
||||||
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
|
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
|
||||||
) -> Result<QuitState, anyhow::Error> {
|
) -> Result<QuitState, anyhow::Error> {
|
||||||
let (tx_git, rx_git) = unbounded();
|
let (tx_git, rx_git) = unbounded();
|
||||||
let (tx_app, rx_app) = unbounded();
|
let (tx_app, rx_app) = unbounded();
|
||||||
|
|
||||||
let rx_input = input.receiver();
|
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 rx_watcher = watcher.receiver();
|
||||||
let spinner_ticker = tick(SPINNER_INTERVAL);
|
let spinner_ticker = tick(SPINNER_INTERVAL);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use crossbeam_channel::{unbounded, Sender};
|
use crossbeam_channel::{unbounded, Sender};
|
||||||
use notify::{Error, RecommendedWatcher, RecursiveMode};
|
use notify::{
|
||||||
|
Config, Error, PollWatcher, RecommendedWatcher, RecursiveMode,
|
||||||
|
Watcher,
|
||||||
|
};
|
||||||
use notify_debouncer_mini::{
|
use notify_debouncer_mini::{
|
||||||
new_debouncer, DebouncedEvent, Debouncer,
|
new_debouncer, new_debouncer_opt, DebouncedEvent,
|
||||||
};
|
};
|
||||||
use scopetime::scope_time;
|
use scopetime::scope_time;
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -11,22 +14,23 @@ use std::{
|
||||||
|
|
||||||
pub struct RepoWatcher {
|
pub struct RepoWatcher {
|
||||||
receiver: crossbeam_channel::Receiver<()>,
|
receiver: crossbeam_channel::Receiver<()>,
|
||||||
#[allow(dead_code)]
|
|
||||||
debouncer: Debouncer<RecommendedWatcher>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RepoWatcher {
|
impl RepoWatcher {
|
||||||
pub fn new(workdir: &str) -> Result<Self> {
|
pub fn new(workdir: &str, poll: bool) -> Self {
|
||||||
scope_time!("RepoWatcher::new");
|
log::trace!(
|
||||||
|
"poll watcher: {poll} recommended: {:?}",
|
||||||
|
RecommendedWatcher::kind()
|
||||||
|
);
|
||||||
|
|
||||||
let (tx, rx) = std::sync::mpsc::channel();
|
let (tx, rx) = std::sync::mpsc::channel();
|
||||||
|
|
||||||
let mut debouncer =
|
let workdir = workdir.to_string();
|
||||||
new_debouncer(Duration::from_secs(2), None, tx)?;
|
|
||||||
|
|
||||||
debouncer
|
thread::spawn(move || {
|
||||||
.watcher()
|
let timeout = Duration::from_secs(2);
|
||||||
.watch(Path::new(workdir), RecursiveMode::Recursive)?;
|
create_watcher(poll, timeout, tx, &workdir);
|
||||||
|
});
|
||||||
|
|
||||||
let (out_tx, out_rx) = unbounded();
|
let (out_tx, out_rx) = unbounded();
|
||||||
|
|
||||||
|
|
@ -37,10 +41,7 @@ impl RepoWatcher {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Self {
|
Self { receiver: out_rx }
|
||||||
debouncer,
|
|
||||||
receiver: out_rx,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
@ -71,3 +72,38 @@ impl RepoWatcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_watcher(
|
||||||
|
poll: bool,
|
||||||
|
timeout: Duration,
|
||||||
|
tx: std::sync::mpsc::Sender<
|
||||||
|
Result<Vec<DebouncedEvent>, Vec<Error>>,
|
||||||
|
>,
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue