Add --only-emit-events (#691)

This commit is contained in:
Félix Saparelli 2023-11-27 23:29:55 +13:00 committed by GitHub
parent d9f6d20b6b
commit 63562fe64d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 116 additions and 12 deletions

View file

@ -19,7 +19,7 @@ _watchexec() {
case "${cmd}" in
watchexec)
opts="-w -c -o -W -r -s -k -d -p -n -E -1 -N -e -f -i -v -h -V --watch --clear --on-busy-update --watch-when-idle --restart --signal --kill --stop-signal --stop-timeout --debounce --stdin-quit --no-vcs-ignore --no-project-ignore --no-global-ignore --no-default-ignore --no-discover-ignore --postpone --delay-run --poll --shell --no-shell-long --no-environment --emit-events-to --env --no-process-group --notify --project-origin --workdir --exts --filter --filter-file --ignore --ignore-file --fs-events --no-meta --print-events --verbose --log-file --manual --completions --help --version [COMMAND]..."
opts="-w -c -o -W -r -s -k -d -p -n -E -1 -N -e -f -i -v -h -V --watch --clear --on-busy-update --watch-when-idle --restart --signal --kill --stop-signal --stop-timeout --debounce --stdin-quit --no-vcs-ignore --no-project-ignore --no-global-ignore --no-default-ignore --no-discover-ignore --postpone --delay-run --poll --shell --no-shell-long --no-environment --emit-events-to --only-emit-events --env --no-process-group --notify --project-origin --workdir --exts --filter --filter-file --ignore --ignore-file --fs-events --no-meta --print-events --verbose --log-file --manual --completions --help --version [COMMAND]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0

View file

@ -66,6 +66,7 @@ set edit:completion:arg-completer[watchexec] = {|@words|
cand -n 'Don''t use a shell'
cand --no-shell-long 'Don''t use a shell'
cand --no-environment 'Shorthand for ''--emit-events=none'''
cand --only-emit-events 'Only emit events to stdout, run no commands'
cand --no-process-group 'Don''t use a process group'
cand -1 'Testing only: exit Watchexec after the first run'
cand -N 'Alert when commands start and end'

View file

@ -33,6 +33,7 @@ complete -c watchexec -s p -l postpone -d 'Wait until first change before runnin
complete -c watchexec -s n -d 'Don\'t use a shell'
complete -c watchexec -l no-shell-long -d 'Don\'t use a shell'
complete -c watchexec -l no-environment -d 'Shorthand for \'--emit-events=none\''
complete -c watchexec -l only-emit-events -d 'Only emit events to stdout, run no commands'
complete -c watchexec -l no-process-group -d 'Don\'t use a process group'
complete -c watchexec -s 1 -d 'Testing only: exit Watchexec after the first run'
complete -c watchexec -s N -l notify -d 'Alert when commands start and end'

View file

@ -47,6 +47,7 @@ module completions {
--no-shell-long # Don't use a shell
--no-environment # Shorthand for '--emit-events=none'
--emit-events-to: string@"nu-complete watchexec emit_events_to" # Configure event emission
--only-emit-events # Only emit events to stdout, run no commands
--env(-E): string # Add env vars to the command
--no-process-group # Don't use a process group
-1 # Testing only: exit Watchexec after the first run

View file

@ -69,6 +69,7 @@ Register-ArgumentCompleter -Native -CommandName 'watchexec' -ScriptBlock {
[CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'Don''t use a shell')
[CompletionResult]::new('--no-shell-long', 'no-shell-long', [CompletionResultType]::ParameterName, 'Don''t use a shell')
[CompletionResult]::new('--no-environment', 'no-environment', [CompletionResultType]::ParameterName, 'Shorthand for ''--emit-events=none''')
[CompletionResult]::new('--only-emit-events', 'only-emit-events', [CompletionResultType]::ParameterName, 'Only emit events to stdout, run no commands')
[CompletionResult]::new('--no-process-group', 'no-process-group', [CompletionResultType]::ParameterName, 'Don''t use a process group')
[CompletionResult]::new('-1', '1', [CompletionResultType]::ParameterName, 'Testing only: exit Watchexec after the first run')
[CompletionResult]::new('-N', 'N ', [CompletionResultType]::ParameterName, 'Alert when commands start and end')

View file

@ -63,6 +63,7 @@ _watchexec() {
'-n[Don'\''t use a shell]' \
'--no-shell-long[Don'\''t use a shell]' \
'--no-environment[Shorthand for '\''--emit-events=none'\'']' \
'(--completions --manual)--only-emit-events[Only emit events to stdout, run no commands]' \
'--no-process-group[Don'\''t use a process group]' \
'-1[Testing only\: exit Watchexec after the first run]' \
'-N[Alert when commands start and end]' \

View file

@ -1,6 +1,6 @@
use std::{path::PathBuf, str::FromStr, time::Duration};
use clap::{ArgAction, Parser, ValueEnum, ValueHint};
use clap::{error::ErrorKind, ArgAction, CommandFactory, Parser, ValueEnum, ValueHint};
use watchexec::paths::PATH_SEPARATOR;
use watchexec_signals::Signal;
@ -88,7 +88,7 @@ pub struct Args {
num_args = 1..,
value_hint = ValueHint::CommandString,
value_name = "COMMAND",
required_unless_present_any = ["completions", "manual"],
required_unless_present_any = ["completions", "manual", "only_emit_events"],
)]
pub command: Vec<String>,
@ -576,9 +576,26 @@ pub struct Args {
default_value = "environment",
hide_default_value = true,
value_name = "MODE",
required_if_eq("only_emit_events", "true"),
)]
pub emit_events_to: EmitEvents,
/// Only emit events to stdout, run no commands.
///
/// This is a convenience option for using Watchexec as a file watcher, without running any
/// commands. It is almost equivalent to using `cat` as the command, except that it will not
/// spawn a new process for each event.
///
/// This option requires `--emit-events-to` to be set, and restricts the available modes to
/// `stdin` and `json-stdin`, modifying their behaviour to write to stdout instead of the stdin
/// of the command.
#[arg(
long,
help_heading = OPTSET_COMMAND,
conflicts_with_all = ["command", "completions", "manual"],
)]
pub only_emit_events: bool,
/// Add env vars to the command
///
/// This is a convenience option for setting environment variables for the command, without
@ -936,6 +953,19 @@ pub fn get_args() -> Args {
];
}
if args.only_emit_events
&& !matches!(
args.emit_events_to,
EmitEvents::JsonStdin | EmitEvents::Stdin
) {
Args::command()
.error(
ErrorKind::InvalidValue,
"only-emit-events requires --emit-events-to=stdin or --emit-events-to=json-stdin",
)
.exit();
}
debug!(?args, "got arguments");
args
}

View file

@ -24,7 +24,7 @@ use watchexec::{
use watchexec_events::{Event, Keyboard, ProcessEnd, Tag};
use watchexec_signals::Signal;
use crate::state::State;
use crate::{state::State, emits::events_to_simple_format};
use crate::{
args::{Args, ClearMode, EmitEvents, OnBusyUpdate},
state::RotatingTempFile,
@ -73,7 +73,58 @@ pub fn make_config(args: &Args, state: &State) -> Result<Config> {
config.file_watcher(Watcher::Poll(interval.0));
}
let once = args.once;
let clear = args.screen_clear;
let emit_events_to = args.emit_events_to;
let emit_file = state.emit_file.clone();
if args.only_emit_events {
config.on_action(move |mut action| {
// if we got a terminate or interrupt signal, quit
if action.signals().any(|sig| sig == Signal::Terminate || sig == Signal::Interrupt) {
action.quit();
return action;
}
// clear the screen before printing events
if let Some(mode) = clear {
match mode {
ClearMode::Clear => {
clearscreen::clear().ok();
}
ClearMode::Reset => {
for cs in [
ClearScreen::WindowsCooked,
ClearScreen::WindowsVt,
ClearScreen::VtLeaveAlt,
ClearScreen::VtWellDone,
ClearScreen::default(),
] {
cs.clear().ok();
}
}
}
}
match emit_events_to {
EmitEvents::Stdin => {
println!("{}", events_to_simple_format(action.events.as_ref()).unwrap_or_default());
}
EmitEvents::JsonStdin => {
for event in action.events.iter().filter(|e| !e.is_empty()) {
println!("{}", serde_json::to_string(event).unwrap_or_default());
}
}
other => unreachable!("emit_events_to should have been validated earlier: {:?}", other),
}
action
});
return Ok(config);
}
let delay_run = args.delay_run.map(|ts| ts.0);
let on_busy = args.on_busy_update;
@ -81,12 +132,9 @@ pub fn make_config(args: &Args, state: &State) -> Result<Config> {
let stop_signal = args.stop_signal;
let stop_timeout = args.stop_timeout.0;
let once = args.once;
let notif = args.notify;
let print_events = args.print_events;
let emit_events_to = args.emit_events_to;
let emit_file = state.emit_file.clone();
let workdir = Arc::new(args.workdir.clone());
let mut add_envs = HashMap::new();

View file

@ -12,7 +12,7 @@ pub fn emits_to_environment(events: &[Event]) -> impl Iterator<Item = (String, O
.map(|(k, v)| (format!("WATCHEXEC_{k}_PATH"), v))
}
fn events_to_simple_format(events: &[Event]) -> Result<String> {
pub fn events_to_simple_format(events: &[Event]) -> Result<String> {
let mut buf = String::new();
for event in events {
let feks = event

View file

@ -4,7 +4,7 @@
.SH NAME
watchexec \- Execute commands when watched files change
.SH SYNOPSIS
\fBwatchexec\fR [\fB\-w\fR|\fB\-\-watch\fR] [\fB\-c\fR|\fB\-\-clear\fR] [\fB\-o\fR|\fB\-\-on\-busy\-update\fR] [\fB\-r\fR|\fB\-\-restart\fR] [\fB\-s\fR|\fB\-\-signal\fR] [\fB\-\-stop\-signal\fR] [\fB\-\-stop\-timeout\fR] [\fB\-d\fR|\fB\-\-debounce\fR] [\fB\-\-stdin\-quit\fR] [\fB\-\-no\-vcs\-ignore\fR] [\fB\-\-no\-project\-ignore\fR] [\fB\-\-no\-global\-ignore\fR] [\fB\-\-no\-default\-ignore\fR] [\fB\-\-no\-discover\-ignore\fR] [\fB\-p\fR|\fB\-\-postpone\fR] [\fB\-\-delay\-run\fR] [\fB\-\-poll\fR] [\fB\-\-shell\fR] [\fB\-n \fR] [\fB\-\-no\-environment\fR] [\fB\-\-emit\-events\-to\fR] [\fB\-E\fR|\fB\-\-env\fR] [\fB\-\-no\-process\-group\fR] [\fB\-N\fR|\fB\-\-notify\fR] [\fB\-\-project\-origin\fR] [\fB\-\-workdir\fR] [\fB\-e\fR|\fB\-\-exts\fR] [\fB\-f\fR|\fB\-\-filter\fR] [\fB\-\-filter\-file\fR] [\fB\-i\fR|\fB\-\-ignore\fR] [\fB\-\-ignore\-file\fR] [\fB\-\-fs\-events\fR] [\fB\-\-no\-meta\fR] [\fB\-\-print\-events\fR] [\fB\-v\fR|\fB\-\-verbose\fR]... [\fB\-\-log\-file\fR] [\fB\-\-manual\fR] [\fB\-\-completions\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fICOMMAND\fR]
\fBwatchexec\fR [\fB\-w\fR|\fB\-\-watch\fR] [\fB\-c\fR|\fB\-\-clear\fR] [\fB\-o\fR|\fB\-\-on\-busy\-update\fR] [\fB\-r\fR|\fB\-\-restart\fR] [\fB\-s\fR|\fB\-\-signal\fR] [\fB\-\-stop\-signal\fR] [\fB\-\-stop\-timeout\fR] [\fB\-d\fR|\fB\-\-debounce\fR] [\fB\-\-stdin\-quit\fR] [\fB\-\-no\-vcs\-ignore\fR] [\fB\-\-no\-project\-ignore\fR] [\fB\-\-no\-global\-ignore\fR] [\fB\-\-no\-default\-ignore\fR] [\fB\-\-no\-discover\-ignore\fR] [\fB\-p\fR|\fB\-\-postpone\fR] [\fB\-\-delay\-run\fR] [\fB\-\-poll\fR] [\fB\-\-shell\fR] [\fB\-n \fR] [\fB\-\-no\-environment\fR] [\fB\-\-emit\-events\-to\fR] [\fB\-\-only\-emit\-events\fR] [\fB\-E\fR|\fB\-\-env\fR] [\fB\-\-no\-process\-group\fR] [\fB\-N\fR|\fB\-\-notify\fR] [\fB\-\-project\-origin\fR] [\fB\-\-workdir\fR] [\fB\-e\fR|\fB\-\-exts\fR] [\fB\-f\fR|\fB\-\-filter\fR] [\fB\-\-filter\-file\fR] [\fB\-i\fR|\fB\-\-ignore\fR] [\fB\-\-ignore\-file\fR] [\fB\-\-fs\-events\fR] [\fB\-\-no\-meta\fR] [\fB\-\-print\-events\fR] [\fB\-v\fR|\fB\-\-verbose\fR]... [\fB\-\-log\-file\fR] [\fB\-\-manual\fR] [\fB\-\-completions\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fICOMMAND\fR]
.SH DESCRIPTION
Execute commands when watched files change.
.PP
@ -341,6 +341,13 @@ environment variable.
Finally, the special \*(Aqnone\*(Aq mode will disable event emission entirely.
.TP
\fB\-\-only\-emit\-events\fR
Only emit events to stdout, run no commands.
This is a convenience option for using Watchexec as a file watcher, without running any commands. It is almost equivalent to using `cat` as the command, except that it will not spawn a new process for each event.
This option requires `\-\-emit\-events\-to` to be set, and restricts the available modes to `stdin` and `json\-stdin`, modifying their behaviour to write to stdout instead of the stdin of the command.
.TP
\fB\-E\fR, \fB\-\-env\fR=\fIKEY=VALUE\fR
Add env vars to the command

View file

@ -13,9 +13,10 @@ watchexec - Execute commands when watched files change
\[**\--no-discover-ignore**\] \[**-p**\|**\--postpone**\]
\[**\--delay-run**\] \[**\--poll**\] \[**\--shell**\] \[**-n **\]
\[**\--no-environment**\] \[**\--emit-events-to**\]
\[**-E**\|**\--env**\] \[**\--no-process-group**\]
\[**-N**\|**\--notify**\] \[**\--project-origin**\] \[**\--workdir**\]
\[**-e**\|**\--exts**\] \[**-f**\|**\--filter**\] \[**\--filter-file**\]
\[**\--only-emit-events**\] \[**-E**\|**\--env**\]
\[**\--no-process-group**\] \[**-N**\|**\--notify**\]
\[**\--project-origin**\] \[**\--workdir**\] \[**-e**\|**\--exts**\]
\[**-f**\|**\--filter**\] \[**\--filter-file**\]
\[**-i**\|**\--ignore**\] \[**\--ignore-file**\] \[**\--fs-events**\]
\[**\--no-meta**\] \[**\--print-events**\]
\[**-v**\|**\--verbose**\]\... \[**\--log-file**\] \[**\--manual**\]
@ -458,6 +459,19 @@ file with the \$WATCHEXEC_EVENTS_FILE environment variable.
Finally, the special none mode will disable event emission entirely.
**\--only-emit-events**
: Only emit events to stdout, run no commands.
This is a convenience option for using Watchexec as a file watcher,
without running any commands. It is almost equivalent to using \`cat\`
as the command, except that it will not spawn a new process for each
event.
This option requires \`\--emit-events-to\` to be set, and restricts the
available modes to \`stdin\` and \`json-stdin\`, modifying their
behaviour to write to stdout instead of the stdin of the command.
**-E**, **\--env**=*KEY=VALUE*
: Add env vars to the command

Binary file not shown.