Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --only-emit-events #691

Merged
merged 2 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion completions/bash
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions completions/elvish
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
1 change: 1 addition & 0 deletions completions/fish
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
1 change: 1 addition & 0 deletions completions/nu
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions completions/powershell
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
1 change: 1 addition & 0 deletions completions/zsh
Original file line number Diff line number Diff line change
Expand Up @@ -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]' \
Expand Down
34 changes: 32 additions & 2 deletions crates/cli/src/args.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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>,

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
56 changes: 52 additions & 4 deletions crates/cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -73,20 +73,68 @@ 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;

let signal = args.signal;
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();
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/emits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion doc/watchexec.1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
20 changes: 17 additions & 3 deletions doc/watchexec.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**\]
Expand Down Expand Up @@ -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
Expand Down
Binary file modified doc/watchexec.1.pdf
Binary file not shown.