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

New option: --signal-map #710

Merged
merged 9 commits into from
Dec 9, 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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ More usage examples: [in the CLI README](./crates/cli/#usage-examples)!

All options in detail: [in the CLI README](./crates/cli/#installation),
in the online help (`watchexec -h`, `watchexec --help`, or `watchexec --manual`),
and [in the manual page](./doc/watchexec.1.md) ([PDF](./doc/watchexec.1.pdf)).
and [in the manual page](./doc/watchexec.1.md).


## Augment
Expand Down
1 change: 0 additions & 1 deletion bin/manpage
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/bin/sh
cargo run -p watchexec-cli -- --manual > doc/watchexec.1
roff2pdf < doc/watchexec.1 > doc/watchexec.1.pdf
pandoc doc/watchexec.1 -t markdown > doc/watchexec.1.md
6 changes: 5 additions & 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 -q -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 --ignore-nothing --postpone --delay-run --poll --shell --no-shell-long --no-environment --emit-events-to --only-emit-events --env --no-process-group --notify --color --timings --quiet --bell --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 -q -e -f -i -v -h -V --watch --clear --on-busy-update --watch-when-idle --restart --signal --kill --stop-signal --stop-timeout --map-signal --debounce --stdin-quit --no-vcs-ignore --no-project-ignore --no-global-ignore --no-default-ignore --no-discover-ignore --ignore-nothing --postpone --delay-run --poll --shell --no-shell-long --no-environment --emit-events-to --only-emit-events --env --no-process-group --notify --color --timings --quiet --bell --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 Expand Up @@ -65,6 +65,10 @@ _watchexec() {
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--map-signal)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--debounce)
COMPREPLY=($(compgen -f "${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 @@ -28,6 +28,7 @@ set edit:completion:arg-completer[watchexec] = {|@words|
cand --signal 'Send a signal to the process when it''s still running'
cand --stop-signal 'Signal to send to stop the command'
cand --stop-timeout 'Time to wait for the command to exit gracefully'
cand --map-signal 'Translate signals from the OS to signals to send to the command'
cand -d 'Time to wait for new events before taking action'
cand --debounce 'Time to wait for new events before taking action'
cand --delay-run 'Sleep before running the command'
Expand Down
1 change: 1 addition & 0 deletions completions/fish
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ complete -c watchexec -s o -l on-busy-update -d 'What to do when receiving event
complete -c watchexec -s s -l signal -d 'Send a signal to the process when it\'s still running' -r
complete -c watchexec -l stop-signal -d 'Signal to send to stop the command' -r
complete -c watchexec -l stop-timeout -d 'Time to wait for the command to exit gracefully' -r
complete -c watchexec -l map-signal -d 'Translate signals from the OS to signals to send to the command' -r
complete -c watchexec -s d -l debounce -d 'Time to wait for new events before taking action' -r
complete -c watchexec -l delay-run -d 'Sleep before running the command' -r
complete -c watchexec -l poll -d 'Poll for filesystem changes' -r
Expand Down
1 change: 1 addition & 0 deletions completions/nu
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ module completions {
--kill(-k) # Hidden legacy shorthand for '--signal=kill'
--stop-signal: string # Signal to send to stop the command
--stop-timeout: string # Time to wait for the command to exit gracefully
--map-signal: string # Translate signals from the OS to signals to send to the command
--debounce(-d): string # Time to wait for new events before taking action
--stdin-quit # Exit when stdin closes
--no-vcs-ignore # Don't load gitignores
Expand Down
1 change: 1 addition & 0 deletions completions/powershell
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Register-ArgumentCompleter -Native -CommandName 'watchexec' -ScriptBlock {
[CompletionResult]::new('--signal', 'signal', [CompletionResultType]::ParameterName, 'Send a signal to the process when it''s still running')
[CompletionResult]::new('--stop-signal', 'stop-signal', [CompletionResultType]::ParameterName, 'Signal to send to stop the command')
[CompletionResult]::new('--stop-timeout', 'stop-timeout', [CompletionResultType]::ParameterName, 'Time to wait for the command to exit gracefully')
[CompletionResult]::new('--map-signal', 'map-signal', [CompletionResultType]::ParameterName, 'Translate signals from the OS to signals to send to the command')
[CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Time to wait for new events before taking action')
[CompletionResult]::new('--debounce', 'debounce', [CompletionResultType]::ParameterName, 'Time to wait for new events before taking action')
[CompletionResult]::new('--delay-run', 'delay-run', [CompletionResultType]::ParameterName, 'Sleep before running the command')
Expand Down
1 change: 1 addition & 0 deletions completions/zsh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ _watchexec() {
'(-r --restart -W --watch-when-idle)--signal=[Send a signal to the process when it'\''s still running]:SIGNAL: ' \
'--stop-signal=[Signal to send to stop the command]:SIGNAL: ' \
'--stop-timeout=[Time to wait for the command to exit gracefully]:TIMEOUT: ' \
'*--map-signal=[Translate signals from the OS to signals to send to the command]:SIGNAL:SIGNAL: ' \
'-d+[Time to wait for new events before taking action]:TIMEOUT: ' \
'--debounce=[Time to wait for new events before taking action]:TIMEOUT: ' \
'--delay-run=[Sleep before running the command]:DURATION: ' \
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,6 @@ If not bundled, you can generate completions for your shell with `watchexec --co
There's a manual page at `doc/watchexec.1`. Install it to `/usr/share/man/man1/`.
If not bundled, you can generate a manual page with `watchexec --manual > /path/to/watchexec.1`, or view it inline with `watchexec --manual` (requires `man`).

You can also [read a text version](../../doc/watchexec.1.md) or a [PDF](../../doc/watchexec.1.pdf).
You can also [read a text version](../../doc/watchexec.1.md).

Note that it is automatically generated from the help text, so it is not as pretty as a carefully hand-written one.
63 changes: 61 additions & 2 deletions crates/cli/src/args.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::{path::PathBuf, str::FromStr, time::Duration};
use std::{ffi::OsStr, path::PathBuf, str::FromStr, time::Duration};

use clap::{error::ErrorKind, ArgAction, CommandFactory, Parser, ValueEnum, ValueHint};
use clap::{
builder::TypedValueParser, error::ErrorKind, Arg, ArgAction, Command, CommandFactory, Parser,
ValueEnum, ValueHint,
};
use watchexec::paths::PATH_SEPARATOR;
use watchexec_signals::Signal;

Expand Down Expand Up @@ -234,6 +237,22 @@ pub struct Args {
)]
pub stop_timeout: TimeSpan,

/// Translate signals from the OS to signals to send to the command
///
/// Takes a pair of signal names, separated by a colon, such as "TERM:INT" to map SIGTERM to
/// SIGINT. The first signal is the one received by watchexec, and the second is the one sent to
/// the command. The second can be omitted to discard the first signal, such as "TERM:" to
/// not do anything on SIGTERM. Note this can make it hard to quit watchexec itself.
///
/// This option can be specified multiple times to map multiple signals.
///
/// Signal syntax is case-insensitive for short names (like "TERM", "USR2") and long names (like
/// "SIGKILL", "SIGHUP"). Signal numbers are also supported (like "15", "31"). On Windows, the
/// forms "STOP", "CTRL+C", "CTRL+BREAK", and "CTRL+CLOSE" are also supported to parse, but will
/// not actually do anything as Watchexec cannot yet deliver nor receive those events.
#[arg(long = "map-signal", value_name = "SIGNAL:SIGNAL", value_parser = SignalMappingValueParser)]
pub signal_map: Vec<SignalMapping>,

/// Time to wait for new events before taking action
///
/// When an event is received, Watchexec will wait for up to this amount of time before handling
Expand Down Expand Up @@ -972,6 +991,46 @@ impl<const UNITLESS_NANOS_MULTIPLIER: u64> FromStr for TimeSpan<UNITLESS_NANOS_M
}
}

#[derive(Clone, Copy, Debug)]
pub struct SignalMapping {
pub from: Signal,
pub to: Option<Signal>,
}

#[derive(Clone)]
struct SignalMappingValueParser;

impl TypedValueParser for SignalMappingValueParser {
type Value = SignalMapping;

fn parse_ref(
&self,
_cmd: &Command,
_arg: Option<&Arg>,
value: &OsStr,
) -> Result<Self::Value, clap::error::Error> {
let value = value
.to_str()
.ok_or_else(|| clap::error::Error::raw(ErrorKind::ValueValidation, "invalid UTF-8"))?;
let (from, to) = value
.split_once(':')
.ok_or_else(|| clap::error::Error::raw(ErrorKind::ValueValidation, "missing ':'"))?;

let from = from
.parse::<Signal>()
.map_err(|sigparse| clap::error::Error::raw(ErrorKind::ValueValidation, sigparse))?;
let to = if to.is_empty() {
None
} else {
Some(to.parse::<Signal>().map_err(|sigparse| {
clap::error::Error::raw(ErrorKind::ValueValidation, sigparse)
})?)
};

Ok(Self::Value { from, to })
}
}

#[inline]
pub fn get_args() -> Args {
use tracing::{debug, warn};
Expand Down
Loading