Implement most existing options and mark the rest
This commit is contained in:
parent
6767948daa
commit
58b37940b8
245
cli/src/args.rs
245
cli/src/args.rs
|
@ -11,128 +11,129 @@ use color_eyre::eyre::{Context, Report, Result};
|
|||
|
||||
pub fn get_args() -> Result<ArgMatches<'static>> {
|
||||
let app = App::new("watchexec")
|
||||
.version(crate_version!())
|
||||
.about("Execute commands when watched files change")
|
||||
.after_help("Use @argfile as first argument to load arguments from the file `argfile` (one argument per line) which will be inserted in place of the @argfile (further arguments on the CLI will override or add onto those in the file).")
|
||||
.arg(Arg::with_name("command")
|
||||
.help("Command to execute")
|
||||
.multiple(true)
|
||||
.required(true))
|
||||
.arg(Arg::with_name("extensions")
|
||||
.help("Comma-separated list of file extensions to watch (e.g. js,css,html)")
|
||||
.short("e")
|
||||
.long("exts")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("path")
|
||||
.help("Watch a specific file or directory")
|
||||
.short("w")
|
||||
.long("watch")
|
||||
.number_of_values(1)
|
||||
.multiple(true)
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("clear")
|
||||
.help("Clear screen before executing command")
|
||||
.short("c")
|
||||
.long("clear"))
|
||||
.arg(Arg::with_name("on-busy-update")
|
||||
.help("Select the behaviour to use when receiving events while the command is running. Current default is queue, will change to do-nothing in 2.0.")
|
||||
.takes_value(true)
|
||||
.possible_values(&["do-nothing", "queue", "restart", "signal"])
|
||||
.long("on-busy-update"))
|
||||
.arg(Arg::with_name("restart")
|
||||
.help("Restart the process if it's still running. Shorthand for --on-busy-update=restart")
|
||||
.short("r")
|
||||
.long("restart"))
|
||||
.arg(Arg::with_name("signal")
|
||||
.help("Send signal to process upon changes, e.g. SIGHUP")
|
||||
.short("s")
|
||||
.long("signal")
|
||||
.takes_value(true)
|
||||
.number_of_values(1)
|
||||
.value_name("signal"))
|
||||
.arg(Arg::with_name("kill")
|
||||
.hidden(true)
|
||||
.short("k")
|
||||
.long("kill"))
|
||||
.arg(Arg::with_name("debounce")
|
||||
.help("Set the timeout between detected change and command execution, defaults to 100ms")
|
||||
.takes_value(true)
|
||||
.value_name("milliseconds")
|
||||
.short("d")
|
||||
.long("debounce"))
|
||||
|
||||
.arg(Arg::with_name("verbose")
|
||||
.help("Print debugging messages to stderr")
|
||||
.short("v")
|
||||
.long("verbose"))
|
||||
.arg(Arg::with_name("changes")
|
||||
.help("Only print path change information. Overridden by --verbose")
|
||||
.long("changes-only"))
|
||||
|
||||
.arg(Arg::with_name("filter")
|
||||
.help("Ignore all modifications except those matching the pattern")
|
||||
.short("f")
|
||||
.long("filter")
|
||||
.number_of_values(1)
|
||||
.multiple(true)
|
||||
.takes_value(true)
|
||||
.value_name("pattern"))
|
||||
.arg(Arg::with_name("ignore")
|
||||
.help("Ignore modifications to paths matching the pattern")
|
||||
.short("i")
|
||||
.long("ignore")
|
||||
.number_of_values(1)
|
||||
.multiple(true)
|
||||
.takes_value(true)
|
||||
.value_name("pattern"))
|
||||
.arg(Arg::with_name("no-vcs-ignore")
|
||||
.help("Skip auto-loading of .gitignore files for filtering")
|
||||
.long("no-vcs-ignore"))
|
||||
.arg(Arg::with_name("no-ignore")
|
||||
.help("Skip auto-loading of ignore files (.gitignore, .ignore, etc.) for filtering")
|
||||
.long("no-ignore"))
|
||||
.arg(Arg::with_name("no-default-ignore")
|
||||
.help("Skip auto-ignoring of commonly ignored globs")
|
||||
.long("no-default-ignore"))
|
||||
.arg(Arg::with_name("postpone")
|
||||
.help("Wait until first change to execute command")
|
||||
.short("p")
|
||||
.long("postpone"))
|
||||
.arg(Arg::with_name("poll")
|
||||
.help("Force polling mode (interval in milliseconds)")
|
||||
.long("force-poll")
|
||||
.value_name("interval"))
|
||||
.arg(Arg::with_name("shell")
|
||||
.help(if cfg!(windows) {
|
||||
"Use a different shell, or `none`. Try --shell=powershell, which will become the default in 2.0."
|
||||
} else {
|
||||
"Use a different shell, or `none`. E.g. --shell=bash"
|
||||
})
|
||||
.takes_value(true)
|
||||
.long("shell"))
|
||||
// -n short form will not be removed, and instead become a shorthand for --shell=none
|
||||
.arg(Arg::with_name("no-shell")
|
||||
.help("Do not wrap command in a shell. Deprecated: use --shell=none instead.")
|
||||
.short("n")
|
||||
.long("no-shell"))
|
||||
.arg(Arg::with_name("no-meta")
|
||||
.help("Ignore metadata changes")
|
||||
.long("no-meta"))
|
||||
.arg(Arg::with_name("no-environment")
|
||||
.help("Do not set WATCHEXEC_*_PATH environment variables for the command")
|
||||
.long("no-environment"))
|
||||
.arg(Arg::with_name("no-process-group")
|
||||
.help("Do not use a process group when running the command")
|
||||
.long("no-process-group"))
|
||||
.arg(Arg::with_name("once").short("1").hidden(true))
|
||||
.arg(Arg::with_name("watch-when-idle")
|
||||
.help("Deprecated alias for --on-busy-update=do-nothing, which will become the default in 2.0.")
|
||||
.short("W")
|
||||
.long("watch-when-idle"))
|
||||
.arg(Arg::with_name("notif")
|
||||
.help("Send a desktop notification when watchexec notices a change (experimental, behaviour may change)")
|
||||
.short("N")
|
||||
.long("notify"));
|
||||
.version(crate_version!())
|
||||
.about("Execute commands when watched files change")
|
||||
.after_help("Use @argfile as first argument to load arguments from the file `argfile` (one argument per line) which will be inserted in place of the @argfile (further arguments on the CLI will override or add onto those in the file).")
|
||||
.arg(Arg::with_name("command")
|
||||
.help("Command to execute")
|
||||
.multiple(true)
|
||||
.required(true))
|
||||
.arg(Arg::with_name("extensions") // TODO
|
||||
.help("Comma-separated list of file extensions to watch (e.g. js,css,html)")
|
||||
.short("e")
|
||||
.long("exts")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("paths")
|
||||
.help("Watch a specific file or directory")
|
||||
.short("w")
|
||||
.long("watch")
|
||||
.number_of_values(1)
|
||||
.multiple(true)
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("clear")
|
||||
.help("Clear screen before executing command")
|
||||
.short("c")
|
||||
.long("clear"))
|
||||
.arg(Arg::with_name("on-busy-update")
|
||||
.help("Select the behaviour to use when receiving events while the command is running. Current default is queue, will change to do-nothing in 2.0.")
|
||||
.takes_value(true)
|
||||
.possible_values(&["do-nothing", "queue", "restart", "signal"])
|
||||
.long("on-busy-update"))
|
||||
.arg(Arg::with_name("restart")
|
||||
.help("Restart the process if it's still running. Shorthand for --on-busy-update=restart")
|
||||
.short("r")
|
||||
.long("restart"))
|
||||
.arg(Arg::with_name("signal")
|
||||
.help("Specify the signal to send when using --on-busy-update=signal")
|
||||
.short("s")
|
||||
.long("signal")
|
||||
.takes_value(true)
|
||||
.value_name("signal")
|
||||
.default_value("SIGTERM")
|
||||
.hidden(cfg!(windows)))
|
||||
.arg(Arg::with_name("kill")
|
||||
.hidden(true)
|
||||
.short("k")
|
||||
.long("kill"))
|
||||
.arg(Arg::with_name("debounce")
|
||||
.help("Set the timeout between detected change and command execution, defaults to 100ms")
|
||||
.takes_value(true)
|
||||
.value_name("milliseconds")
|
||||
.short("d")
|
||||
.long("debounce"))
|
||||
.arg(Arg::with_name("verbose")
|
||||
.help("Print debugging messages (-v, -vv, -vvv; use -vvv for bug reports)")
|
||||
.multiple(true)
|
||||
.short("v")
|
||||
.long("verbose"))
|
||||
.arg(Arg::with_name("print-events")
|
||||
.help("Print events that trigger actions")
|
||||
.long("print-events")
|
||||
.alias("changes-only")) // --changes-only is deprecated (remove at v2)
|
||||
.arg(Arg::with_name("filter") // TODO
|
||||
.help("Ignore all modifications except those matching the pattern")
|
||||
.short("f")
|
||||
.long("filter")
|
||||
.number_of_values(1)
|
||||
.multiple(true)
|
||||
.takes_value(true)
|
||||
.value_name("pattern"))
|
||||
.arg(Arg::with_name("ignore") // TODO
|
||||
.help("Ignore modifications to paths matching the pattern")
|
||||
.short("i")
|
||||
.long("ignore")
|
||||
.number_of_values(1)
|
||||
.multiple(true)
|
||||
.takes_value(true)
|
||||
.value_name("pattern"))
|
||||
.arg(Arg::with_name("no-vcs-ignore") // TODO
|
||||
.help("Skip auto-loading of .gitignore files for filtering")
|
||||
.long("no-vcs-ignore"))
|
||||
.arg(Arg::with_name("no-ignore") // TODO
|
||||
.help("Skip auto-loading of ignore files (.gitignore, .ignore, etc.) for filtering")
|
||||
.long("no-ignore"))
|
||||
.arg(Arg::with_name("no-default-ignore") // TODO
|
||||
.help("Skip auto-ignoring of commonly ignored globs")
|
||||
.long("no-default-ignore"))
|
||||
.arg(Arg::with_name("postpone")
|
||||
.help("Wait until first change to execute command")
|
||||
.short("p")
|
||||
.long("postpone"))
|
||||
.arg(Arg::with_name("poll")
|
||||
.help("Force polling mode (interval in milliseconds)")
|
||||
.long("force-poll")
|
||||
.value_name("interval"))
|
||||
.arg(Arg::with_name("shell")
|
||||
.help(if cfg!(windows) {
|
||||
"Use a different shell, or `none`. Try --shell=powershell, which will become the default in 2.0."
|
||||
} else {
|
||||
"Use a different shell, or `none`. E.g. --shell=bash"
|
||||
})
|
||||
.takes_value(true)
|
||||
.long("shell"))
|
||||
// -n short form will not be removed, and instead become a shorthand for --shell=none
|
||||
.arg(Arg::with_name("no-shell")
|
||||
.help("Do not wrap command in a shell. Deprecated: use --shell=none instead.")
|
||||
.short("n")
|
||||
.long("no-shell"))
|
||||
.arg(Arg::with_name("no-meta") // TODO
|
||||
.help("Ignore metadata changes")
|
||||
.long("no-meta"))
|
||||
.arg(Arg::with_name("no-environment") // TODO
|
||||
.help("Do not set WATCHEXEC_*_PATH environment variables for the command")
|
||||
.long("no-environment"))
|
||||
.arg(Arg::with_name("no-process-group") // TODO
|
||||
.help("Do not use a process group when running the command")
|
||||
.long("no-process-group"))
|
||||
.arg(Arg::with_name("once").short("1").hidden(true))
|
||||
.arg(Arg::with_name("watch-when-idle")
|
||||
.help("Deprecated alias for --on-busy-update=do-nothing, which will become the default in 2.0.")
|
||||
.short("W")
|
||||
.long("watch-when-idle"))
|
||||
.arg(Arg::with_name("notif") // TODO
|
||||
.help("Send a desktop notification when watchexec notices a change (experimental, behaviour may change)")
|
||||
.short("N")
|
||||
.long("notify"));
|
||||
|
||||
let mut raw_args: Vec<OsString> = env::args_os().collect();
|
||||
|
||||
|
|
|
@ -1,25 +1,139 @@
|
|||
use std::{
|
||||
convert::Infallible, env::current_dir, io::stderr, path::Path, str::FromStr, time::Duration,
|
||||
};
|
||||
|
||||
use clap::ArgMatches;
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use watchexec::{
|
||||
action::{Action, Outcome, Signal},
|
||||
command::Shell,
|
||||
config::{InitConfig, RuntimeConfig},
|
||||
event::Event,
|
||||
fs::Watcher,
|
||||
handler::PrintDisplay,
|
||||
};
|
||||
|
||||
pub fn new(args: &ArgMatches<'static>) -> Result<(InitConfig, RuntimeConfig)> {
|
||||
Ok((init(&args)?, runtime(&args)?))
|
||||
Ok((init(args)?, runtime(args)?))
|
||||
}
|
||||
|
||||
fn init(args: &ArgMatches<'static>) -> Result<InitConfig> {
|
||||
let mut config = InitConfig::builder();
|
||||
fn init(_args: &ArgMatches<'static>) -> Result<InitConfig> {
|
||||
let mut config = InitConfig::builder();
|
||||
|
||||
Ok(config.build()?)
|
||||
config.on_error(PrintDisplay(stderr()));
|
||||
|
||||
Ok(config.build()?)
|
||||
}
|
||||
|
||||
fn runtime(args: &ArgMatches<'static>) -> Result<RuntimeConfig> {
|
||||
let mut config = RuntimeConfig::default();
|
||||
let mut config = RuntimeConfig::default();
|
||||
|
||||
config.command(
|
||||
args.values_of_lossy("command")
|
||||
.ok_or_else(|| eyre!("(clap) Bug: command is not present"))?
|
||||
.iter(),
|
||||
);
|
||||
|
||||
Ok(config)
|
||||
config.pathset(match args.values_of_os("paths") {
|
||||
Some(paths) => paths.map(|os| Path::new(os).to_owned()).collect(),
|
||||
None => vec![current_dir()?],
|
||||
});
|
||||
|
||||
config.action_throttle(Duration::from_millis(
|
||||
args.value_of("debounce").unwrap_or("100").parse()?,
|
||||
));
|
||||
|
||||
if let Some(interval) = args.value_of("poll") {
|
||||
config.file_watcher(Watcher::Poll(Duration::from_millis(interval.parse()?)));
|
||||
}
|
||||
|
||||
config.command_shell(if args.is_present("no-shell") {
|
||||
Shell::None
|
||||
} else if let Some(s) = args.value_of("shell") {
|
||||
if s.eq_ignore_ascii_case("powershell") {
|
||||
Shell::Powershell
|
||||
} else if s.eq_ignore_ascii_case("none") {
|
||||
Shell::None
|
||||
} else if s.eq_ignore_ascii_case("cmd") {
|
||||
cmd_shell(s.into())
|
||||
} else {
|
||||
Shell::Unix(s.into())
|
||||
}
|
||||
} else {
|
||||
default_shell()
|
||||
});
|
||||
|
||||
let clear = args.is_present("clear");
|
||||
let mut on_busy = args
|
||||
.value_of("on-busy-update")
|
||||
.unwrap_or("queue")
|
||||
.to_owned();
|
||||
|
||||
if args.is_present("restart") {
|
||||
on_busy = "restart".into();
|
||||
}
|
||||
|
||||
if args.is_present("watch-when-idle") {
|
||||
on_busy = "do-nothing".into();
|
||||
}
|
||||
|
||||
let mut signal = args
|
||||
.value_of("signal")
|
||||
.map(|s| Signal::from_str(s))
|
||||
.transpose()?
|
||||
.unwrap_or(Signal::SIGTERM);
|
||||
|
||||
if args.is_present("kill") {
|
||||
signal = Signal::SIGKILL;
|
||||
}
|
||||
|
||||
let print_events = args.is_present("print-events");
|
||||
let once = args.is_present("once");
|
||||
|
||||
config.on_action(move |action: Action| {
|
||||
let fut = async { Ok::<(), Infallible>(()) };
|
||||
|
||||
if print_events {
|
||||
for event in &action.events {
|
||||
for path in event.paths() {
|
||||
eprintln!("[EVENT] Path: {}", path.display());
|
||||
}
|
||||
|
||||
for signal in event.signals() {
|
||||
eprintln!("[EVENT] Signal: {:?}", signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if once && !action.events.iter().any(|e| e == &Event::default()) {
|
||||
action.outcome(Outcome::Exit);
|
||||
return fut;
|
||||
}
|
||||
|
||||
let when_running = match (clear, on_busy.as_str()) {
|
||||
(_, "do-nothing") => Outcome::DoNothing,
|
||||
(true, "restart") => {
|
||||
Outcome::both(Outcome::Stop, Outcome::both(Outcome::Clear, Outcome::Start))
|
||||
}
|
||||
(false, "restart") => Outcome::both(Outcome::Stop, Outcome::Start),
|
||||
(_, "signal") => Outcome::Signal(signal),
|
||||
// (true, "queue") => Outcome::wait(Outcome::both(Outcome::Clear, Outcome::Start)),
|
||||
// (false, "queue") => Outcome::wait(Outcome::Start),
|
||||
_ => Outcome::DoNothing,
|
||||
};
|
||||
|
||||
let when_idle = if clear {
|
||||
Outcome::both(Outcome::Clear, Outcome::Start)
|
||||
} else {
|
||||
Outcome::Start
|
||||
};
|
||||
|
||||
action.outcome(Outcome::if_running(when_running, when_idle));
|
||||
|
||||
fut
|
||||
});
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
// until 2.0
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::env::var;
|
|||
|
||||
use color_eyre::eyre::Result;
|
||||
use tracing_subscriber::filter::LevelFilter;
|
||||
use watchexec::Watchexec;
|
||||
use watchexec::{event::Event, Watchexec};
|
||||
|
||||
mod args;
|
||||
mod config;
|
||||
|
@ -19,16 +19,24 @@ async fn main() -> Result<()> {
|
|||
|
||||
if args.is_present("verbose") {
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(LevelFilter::DEBUG)
|
||||
.with_max_level(match args.occurrences_of("verbose") {
|
||||
0 => unreachable!(),
|
||||
1 => LevelFilter::INFO,
|
||||
2 => LevelFilter::DEBUG,
|
||||
_ => LevelFilter::TRACE,
|
||||
})
|
||||
.try_init()
|
||||
.ok();
|
||||
}
|
||||
|
||||
let (init, runtime) = config::new(&args)?;
|
||||
|
||||
let config = runtime.clone();
|
||||
let wx = Watchexec::new(init, runtime)?;
|
||||
|
||||
if !args.is_present("postpone") {
|
||||
wx.send_event(Event::default()).await?;
|
||||
}
|
||||
|
||||
wx.main().await??;
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in New Issue