Compare commits

...

4 Commits

Author SHA1 Message Date
Félix Saparelli 62bf72a3a6
Update completions and manpage 2024-04-28 16:49:18 +12:00
Félix Saparelli d72362002c
feat(cli): use non-blocking logging 2024-04-28 16:48:51 +12:00
Félix Saparelli ad27bb2874
Cleanup logging handling 2024-04-28 15:02:23 +12:00
Félix Saparelli 0962d178a9
fix unset args 2024-04-28 14:34:39 +12:00
14 changed files with 270 additions and 191 deletions

13
Cargo.lock generated
View File

@ -3685,6 +3685,18 @@ dependencies = [
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-appender"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
dependencies = [
"crossbeam-channel",
"thiserror",
"time",
"tracing-subscriber",
]
[[package]] [[package]]
name = "tracing-attributes" name = "tracing-attributes"
version = "0.1.27" version = "0.1.27"
@ -4060,6 +4072,7 @@ dependencies = [
"termcolor", "termcolor",
"tokio", "tokio",
"tracing", "tracing",
"tracing-appender",
"tracing-subscriber", "tracing-subscriber",
"tracing-test", "tracing-test",
"uuid", "uuid",

View File

@ -19,7 +19,7 @@ _watchexec() {
case "${cmd}" in case "${cmd}" in
watchexec) watchexec)
opts="-w -c -o -r -s -d -p -n -E -1 -N -q -e -f -j -i -v -h -V --watch --clear --on-busy-update --restart --signal --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-environment --emit-events-to --only-emit-events --env --no-process-group --wrap-process --notify --color --timings --quiet --bell --project-origin --workdir --exts --filter --filter-file --filter-prog --ignore --ignore-file --fs-events --no-meta --print-events --verbose --log-file --manual --completions --help --version [COMMAND]..." opts="-w -W -c -o -r -s -d -p -n -E -1 -N -q -e -f -j -i -v -h -V --watch --watch-non-recursive --clear --on-busy-update --restart --signal --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-environment --emit-events-to --only-emit-events --env --no-process-group --wrap-process --notify --color --timings --quiet --bell --project-origin --workdir --exts --filter --filter-file --filter-prog --ignore --ignore-file --fs-events --no-meta --print-events --manual --completions --verbose --log-file --help --version [COMMAND]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0
@ -33,6 +33,14 @@ _watchexec() {
COMPREPLY=($(compgen -f "${cur}")) COMPREPLY=($(compgen -f "${cur}"))
return 0 return 0
;; ;;
--watch-non-recursive)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-W)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--clear) --clear)
COMPREPLY=($(compgen -W "clear reset" -- "${cur}")) COMPREPLY=($(compgen -W "clear reset" -- "${cur}"))
return 0 return 0
@ -189,14 +197,14 @@ _watchexec() {
COMPREPLY=($(compgen -W "access create remove rename modify metadata" -- "${cur}")) COMPREPLY=($(compgen -W "access create remove rename modify metadata" -- "${cur}"))
return 0 return 0
;; ;;
--log-file)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--completions) --completions)
COMPREPLY=($(compgen -W "bash elvish fish nu powershell zsh" -- "${cur}")) COMPREPLY=($(compgen -W "bash elvish fish nu powershell zsh" -- "${cur}"))
return 0 return 0
;; ;;
--log-file)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*) *)
COMPREPLY=() COMPREPLY=()
;; ;;

View File

@ -20,6 +20,8 @@ set edit:completion:arg-completer[watchexec] = {|@words|
&'watchexec'= { &'watchexec'= {
cand -w 'Watch a specific file or directory' cand -w 'Watch a specific file or directory'
cand --watch 'Watch a specific file or directory' cand --watch 'Watch a specific file or directory'
cand -W 'Watch a specific directory, non-recursively'
cand --watch-non-recursive 'Watch a specific directory, non-recursively'
cand -c 'Clear screen before running command' cand -c 'Clear screen before running command'
cand --clear 'Clear screen before running command' cand --clear 'Clear screen before running command'
cand -o 'What to do when receiving events while the command is running' cand -o 'What to do when receiving events while the command is running'
@ -52,8 +54,8 @@ set edit:completion:arg-completer[watchexec] = {|@words|
cand --ignore 'Filename patterns to filter out' cand --ignore 'Filename patterns to filter out'
cand --ignore-file 'Files to load ignores from' cand --ignore-file 'Files to load ignores from'
cand --fs-events 'Filesystem events to filter to' cand --fs-events 'Filesystem events to filter to'
cand --log-file 'Write diagnostic logs to a file'
cand --completions 'Generate a shell completions script' cand --completions 'Generate a shell completions script'
cand --log-file 'Write diagnostic logs to a file'
cand -r 'Restart the process if it''s still running' cand -r 'Restart the process if it''s still running'
cand --restart 'Restart the process if it''s still running' cand --restart 'Restart the process if it''s still running'
cand --stdin-quit 'Exit when stdin closes' cand --stdin-quit 'Exit when stdin closes'
@ -78,9 +80,9 @@ set edit:completion:arg-completer[watchexec] = {|@words|
cand --bell 'Ring the terminal bell on command completion' cand --bell 'Ring the terminal bell on command completion'
cand --no-meta 'Don''t emit fs events for metadata changes' cand --no-meta 'Don''t emit fs events for metadata changes'
cand --print-events 'Print events that trigger actions' cand --print-events 'Print events that trigger actions'
cand --manual 'Show the manual page'
cand -v 'Set diagnostic log level' cand -v 'Set diagnostic log level'
cand --verbose 'Set diagnostic log level' cand --verbose 'Set diagnostic log level'
cand --manual 'Show the manual page'
cand -h 'Print help (see more with ''--help'')' cand -h 'Print help (see more with ''--help'')'
cand --help 'Print help (see more with ''--help'')' cand --help 'Print help (see more with ''--help'')'
cand -V 'Print version' cand -V 'Print version'

View File

@ -1,4 +1,5 @@
complete -c watchexec -s w -l watch -d 'Watch a specific file or directory' -r -F complete -c watchexec -s w -l watch -d 'Watch a specific file or directory' -r -F
complete -c watchexec -s W -l watch-non-recursive -d 'Watch a specific directory, non-recursively' -r -F
complete -c watchexec -s c -l clear -d 'Clear screen before running command' -r -f -a "{clear '',reset ''}" complete -c watchexec -s c -l clear -d 'Clear screen before running command' -r -f -a "{clear '',reset ''}"
complete -c watchexec -s o -l on-busy-update -d 'What to do when receiving events while the command is running' -r -f -a "{queue '',do-nothing '',restart '',signal ''}" complete -c watchexec -s o -l on-busy-update -d 'What to do when receiving events while the command is running' -r -f -a "{queue '',do-nothing '',restart '',signal ''}"
complete -c watchexec -s s -l signal -d 'Send a signal to the process when it\'s still running' -r complete -c watchexec -s s -l signal -d 'Send a signal to the process when it\'s still running' -r
@ -22,8 +23,8 @@ complete -c watchexec -s j -l filter-prog -d '[experimental] Filter programs' -r
complete -c watchexec -s i -l ignore -d 'Filename patterns to filter out' -r complete -c watchexec -s i -l ignore -d 'Filename patterns to filter out' -r
complete -c watchexec -l ignore-file -d 'Files to load ignores from' -r -F complete -c watchexec -l ignore-file -d 'Files to load ignores from' -r -F
complete -c watchexec -l fs-events -d 'Filesystem events to filter to' -r -f -a "{access '',create '',remove '',rename '',modify '',metadata ''}" complete -c watchexec -l fs-events -d 'Filesystem events to filter to' -r -f -a "{access '',create '',remove '',rename '',modify '',metadata ''}"
complete -c watchexec -l log-file -d 'Write diagnostic logs to a file' -r -F
complete -c watchexec -l completions -d 'Generate a shell completions script' -r -f -a "{bash '',elvish '',fish '',nu '',powershell '',zsh ''}" complete -c watchexec -l completions -d 'Generate a shell completions script' -r -f -a "{bash '',elvish '',fish '',nu '',powershell '',zsh ''}"
complete -c watchexec -l log-file -d 'Write diagnostic logs to a file' -r -F
complete -c watchexec -s r -l restart -d 'Restart the process if it\'s still running' complete -c watchexec -s r -l restart -d 'Restart the process if it\'s still running'
complete -c watchexec -l stdin-quit -d 'Exit when stdin closes' complete -c watchexec -l stdin-quit -d 'Exit when stdin closes'
complete -c watchexec -l no-vcs-ignore -d 'Don\'t load gitignores' complete -c watchexec -l no-vcs-ignore -d 'Don\'t load gitignores'
@ -44,7 +45,7 @@ complete -c watchexec -s q -l quiet -d 'Don\'t print starting and stopping messa
complete -c watchexec -l bell -d 'Ring the terminal bell on command completion' complete -c watchexec -l bell -d 'Ring the terminal bell on command completion'
complete -c watchexec -l no-meta -d 'Don\'t emit fs events for metadata changes' complete -c watchexec -l no-meta -d 'Don\'t emit fs events for metadata changes'
complete -c watchexec -l print-events -d 'Print events that trigger actions' complete -c watchexec -l print-events -d 'Print events that trigger actions'
complete -c watchexec -s v -l verbose -d 'Set diagnostic log level'
complete -c watchexec -l manual -d 'Show the manual page' complete -c watchexec -l manual -d 'Show the manual page'
complete -c watchexec -s v -l verbose -d 'Set diagnostic log level'
complete -c watchexec -s h -l help -d 'Print help (see more with \'--help\')' complete -c watchexec -s h -l help -d 'Print help (see more with \'--help\')'
complete -c watchexec -s V -l version -d 'Print version' complete -c watchexec -s V -l version -d 'Print version'

View File

@ -32,6 +32,7 @@ module completions {
export extern watchexec [ export extern watchexec [
...command: string # Command to run on changes ...command: string # Command to run on changes
--watch(-w): string # Watch a specific file or directory --watch(-w): string # Watch a specific file or directory
--watch-non-recursive(-W): string # Watch a specific directory, non-recursively
--clear(-c): string@"nu-complete watchexec screen_clear" # Clear screen before running command --clear(-c): string@"nu-complete watchexec screen_clear" # Clear screen before running command
--on-busy-update(-o): string@"nu-complete watchexec on_busy_update" # What to do when receiving events while the command is running --on-busy-update(-o): string@"nu-complete watchexec on_busy_update" # What to do when receiving events while the command is running
--restart(-r) # Restart the process if it's still running --restart(-r) # Restart the process if it's still running
@ -75,10 +76,10 @@ module completions {
--fs-events: string@"nu-complete watchexec filter_fs_events" # Filesystem events to filter to --fs-events: string@"nu-complete watchexec filter_fs_events" # Filesystem events to filter to
--no-meta # Don't emit fs events for metadata changes --no-meta # Don't emit fs events for metadata changes
--print-events # Print events that trigger actions --print-events # Print events that trigger actions
--verbose(-v) # Set diagnostic log level
--log-file: string # Write diagnostic logs to a file
--manual # Show the manual page --manual # Show the manual page
--completions: string@"nu-complete watchexec completions" # Generate a shell completions script --completions: string@"nu-complete watchexec completions" # Generate a shell completions script
--verbose(-v) # Set diagnostic log level
--log-file: string # Write diagnostic logs to a file
--help(-h) # Print help (see more with '--help') --help(-h) # Print help (see more with '--help')
--version(-V) # Print version --version(-V) # Print version
] ]

View File

@ -23,6 +23,8 @@ Register-ArgumentCompleter -Native -CommandName 'watchexec' -ScriptBlock {
'watchexec' { 'watchexec' {
[CompletionResult]::new('-w', 'w', [CompletionResultType]::ParameterName, 'Watch a specific file or directory') [CompletionResult]::new('-w', 'w', [CompletionResultType]::ParameterName, 'Watch a specific file or directory')
[CompletionResult]::new('--watch', 'watch', [CompletionResultType]::ParameterName, 'Watch a specific file or directory') [CompletionResult]::new('--watch', 'watch', [CompletionResultType]::ParameterName, 'Watch a specific file or directory')
[CompletionResult]::new('-W', 'W ', [CompletionResultType]::ParameterName, 'Watch a specific directory, non-recursively')
[CompletionResult]::new('--watch-non-recursive', 'watch-non-recursive', [CompletionResultType]::ParameterName, 'Watch a specific directory, non-recursively')
[CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'Clear screen before running command') [CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'Clear screen before running command')
[CompletionResult]::new('--clear', 'clear', [CompletionResultType]::ParameterName, 'Clear screen before running command') [CompletionResult]::new('--clear', 'clear', [CompletionResultType]::ParameterName, 'Clear screen before running command')
[CompletionResult]::new('-o', 'o', [CompletionResultType]::ParameterName, 'What to do when receiving events while the command is running') [CompletionResult]::new('-o', 'o', [CompletionResultType]::ParameterName, 'What to do when receiving events while the command is running')
@ -55,8 +57,8 @@ Register-ArgumentCompleter -Native -CommandName 'watchexec' -ScriptBlock {
[CompletionResult]::new('--ignore', 'ignore', [CompletionResultType]::ParameterName, 'Filename patterns to filter out') [CompletionResult]::new('--ignore', 'ignore', [CompletionResultType]::ParameterName, 'Filename patterns to filter out')
[CompletionResult]::new('--ignore-file', 'ignore-file', [CompletionResultType]::ParameterName, 'Files to load ignores from') [CompletionResult]::new('--ignore-file', 'ignore-file', [CompletionResultType]::ParameterName, 'Files to load ignores from')
[CompletionResult]::new('--fs-events', 'fs-events', [CompletionResultType]::ParameterName, 'Filesystem events to filter to') [CompletionResult]::new('--fs-events', 'fs-events', [CompletionResultType]::ParameterName, 'Filesystem events to filter to')
[CompletionResult]::new('--log-file', 'log-file', [CompletionResultType]::ParameterName, 'Write diagnostic logs to a file')
[CompletionResult]::new('--completions', 'completions', [CompletionResultType]::ParameterName, 'Generate a shell completions script') [CompletionResult]::new('--completions', 'completions', [CompletionResultType]::ParameterName, 'Generate a shell completions script')
[CompletionResult]::new('--log-file', 'log-file', [CompletionResultType]::ParameterName, 'Write diagnostic logs to a file')
[CompletionResult]::new('-r', 'r', [CompletionResultType]::ParameterName, 'Restart the process if it''s still running') [CompletionResult]::new('-r', 'r', [CompletionResultType]::ParameterName, 'Restart the process if it''s still running')
[CompletionResult]::new('--restart', 'restart', [CompletionResultType]::ParameterName, 'Restart the process if it''s still running') [CompletionResult]::new('--restart', 'restart', [CompletionResultType]::ParameterName, 'Restart the process if it''s still running')
[CompletionResult]::new('--stdin-quit', 'stdin-quit', [CompletionResultType]::ParameterName, 'Exit when stdin closes') [CompletionResult]::new('--stdin-quit', 'stdin-quit', [CompletionResultType]::ParameterName, 'Exit when stdin closes')
@ -81,9 +83,9 @@ Register-ArgumentCompleter -Native -CommandName 'watchexec' -ScriptBlock {
[CompletionResult]::new('--bell', 'bell', [CompletionResultType]::ParameterName, 'Ring the terminal bell on command completion') [CompletionResult]::new('--bell', 'bell', [CompletionResultType]::ParameterName, 'Ring the terminal bell on command completion')
[CompletionResult]::new('--no-meta', 'no-meta', [CompletionResultType]::ParameterName, 'Don''t emit fs events for metadata changes') [CompletionResult]::new('--no-meta', 'no-meta', [CompletionResultType]::ParameterName, 'Don''t emit fs events for metadata changes')
[CompletionResult]::new('--print-events', 'print-events', [CompletionResultType]::ParameterName, 'Print events that trigger actions') [CompletionResult]::new('--print-events', 'print-events', [CompletionResultType]::ParameterName, 'Print events that trigger actions')
[CompletionResult]::new('--manual', 'manual', [CompletionResultType]::ParameterName, 'Show the manual page')
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Set diagnostic log level') [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Set diagnostic log level')
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Set diagnostic log level') [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Set diagnostic log level')
[CompletionResult]::new('--manual', 'manual', [CompletionResultType]::ParameterName, 'Show the manual page')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version') [CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')

View File

@ -17,6 +17,8 @@ _watchexec() {
_arguments "${_arguments_options[@]}" \ _arguments "${_arguments_options[@]}" \
'*-w+[Watch a specific file or directory]:PATH:_files' \ '*-w+[Watch a specific file or directory]:PATH:_files' \
'*--watch=[Watch a specific file or directory]:PATH:_files' \ '*--watch=[Watch a specific file or directory]:PATH:_files' \
'*-W+[Watch a specific directory, non-recursively]:PATH:_files' \
'*--watch-non-recursive=[Watch a specific directory, non-recursively]:PATH:_files' \
'-c+[Clear screen before running command]' \ '-c+[Clear screen before running command]' \
'--clear=[Clear screen before running command]' \ '--clear=[Clear screen before running command]' \
'-o+[What to do when receiving events while the command is running]:MODE:(queue do-nothing restart signal)' \ '-o+[What to do when receiving events while the command is running]:MODE:(queue do-nothing restart signal)' \
@ -49,8 +51,8 @@ _watchexec() {
'*--ignore=[Filename patterns to filter out]:PATTERN: ' \ '*--ignore=[Filename patterns to filter out]:PATTERN: ' \
'*--ignore-file=[Files to load ignores from]:PATH:_files' \ '*--ignore-file=[Files to load ignores from]:PATH:_files' \
'*--fs-events=[Filesystem events to filter to]:EVENTS:(access create remove rename modify metadata)' \ '*--fs-events=[Filesystem events to filter to]:EVENTS:(access create remove rename modify metadata)' \
'--log-file=[Write diagnostic logs to a file]' \
'(--manual)--completions=[Generate a shell completions script]:COMPLETIONS:(bash elvish fish nu powershell zsh)' \ '(--manual)--completions=[Generate a shell completions script]:COMPLETIONS:(bash elvish fish nu powershell zsh)' \
'--log-file=[Write diagnostic logs to a file]' \
'(-o --on-busy-update)-r[Restart the process if it'\''s still running]' \ '(-o --on-busy-update)-r[Restart the process if it'\''s still running]' \
'(-o --on-busy-update)--restart[Restart the process if it'\''s still running]' \ '(-o --on-busy-update)--restart[Restart the process if it'\''s still running]' \
'--stdin-quit[Exit when stdin closes]' \ '--stdin-quit[Exit when stdin closes]' \
@ -75,9 +77,9 @@ _watchexec() {
'--bell[Ring the terminal bell on command completion]' \ '--bell[Ring the terminal bell on command completion]' \
'(--fs-events)--no-meta[Don'\''t emit fs events for metadata changes]' \ '(--fs-events)--no-meta[Don'\''t emit fs events for metadata changes]' \
'--print-events[Print events that trigger actions]' \ '--print-events[Print events that trigger actions]' \
'(--completions)--manual[Show the manual page]' \
'*-v[Set diagnostic log level]' \ '*-v[Set diagnostic log level]' \
'*--verbose[Set diagnostic log level]' \ '*--verbose[Set diagnostic log level]' \
'(--completions)--manual[Show the manual page]' \
'-h[Print help (see more with '\''--help'\'')]' \ '-h[Print help (see more with '\''--help'\'')]' \
'--help[Print help (see more with '\''--help'\'')]' \ '--help[Print help (see more with '\''--help'\'')]' \
'-V[Print version]' \ '-V[Print version]' \

View File

@ -44,6 +44,7 @@ serde_json = "1.0.107"
tempfile = "3.8.1" tempfile = "3.8.1"
termcolor = "1.4.0" termcolor = "1.4.0"
tracing = "0.1.40" tracing = "0.1.40"
tracing-appender = "0.2.3"
which = "6.0.1" which = "6.0.1"
[dependencies.blake3] [dependencies.blake3]

View File

@ -9,16 +9,20 @@ use std::{
}; };
use clap::{ use clap::{
builder::TypedValueParser, error::ErrorKind, Arg, ArgAction, Command, CommandFactory, Parser, builder::TypedValueParser, error::ErrorKind, Arg, Command, CommandFactory, Parser, ValueEnum,
ValueEnum, ValueHint, ValueHint,
}; };
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result};
use tokio::{fs::File, io::AsyncReadExt}; use tokio::{fs::File, io::AsyncReadExt};
use tracing::{debug, info, trace, warn};
use tracing_appender::non_blocking::WorkerGuard;
use watchexec::{paths::PATH_SEPARATOR, sources::fs::WatchedPath}; use watchexec::{paths::PATH_SEPARATOR, sources::fs::WatchedPath};
use watchexec_signals::Signal; use watchexec_signals::Signal;
use crate::filterer::parse::parse_filter_program; use crate::filterer::parse::parse_filter_program;
mod logging;
const OPTSET_FILTERING: &str = "Filtering"; const OPTSET_FILTERING: &str = "Filtering";
const OPTSET_COMMAND: &str = "Command"; const OPTSET_COMMAND: &str = "Command";
const OPTSET_DEBUGGING: &str = "Debugging"; const OPTSET_DEBUGGING: &str = "Debugging";
@ -949,47 +953,6 @@ pub struct Args {
)] )]
pub print_events: bool, pub print_events: bool,
/// Set diagnostic log level
///
/// This enables diagnostic logging, which is useful for investigating bugs or gaining more
/// insight into faulty filters or "missing" events. Use multiple times to increase verbosity.
///
/// Goes up to '-vvvv'. When submitting bug reports, default to a '-vvv' log level.
///
/// You may want to use with '--log-file' to avoid polluting your terminal.
///
/// Setting $RUST_LOG also works, and takes precedence, but is not recommended. However, using
/// $RUST_LOG is the only way to get logs from before these options are parsed.
#[arg(
long,
short,
help_heading = OPTSET_DEBUGGING,
action = ArgAction::Count,
num_args = 0,
)]
pub verbose: Option<u8>,
/// Write diagnostic logs to a file
///
/// This writes diagnostic logs to a file, instead of the terminal, in JSON format. If a log
/// level was not already specified, this will set it to '-vvv'.
///
/// If a path is not provided, the default is the working directory. Note that with
/// '--ignore-nothing', the write events to the log will likely get picked up by Watchexec,
/// causing a loop; prefer setting a path outside of the watched directory.
///
/// If the path provided is a directory, a file will be created in that directory. The file name
/// will be the current date and time, in the format 'watchexec.YYYY-MM-DDTHH-MM-SSZ.log'.
#[arg(
long,
help_heading = OPTSET_DEBUGGING,
num_args = 0..=1,
default_missing_value = ".",
value_hint = ValueHint::AnyPath,
value_name = "PATH",
)]
pub log_file: Option<PathBuf>,
/// Show the manual page /// Show the manual page
/// ///
/// This shows the manual page for Watchexec, if the output is a terminal and the 'man' program /// This shows the manual page for Watchexec, if the output is a terminal and the 'man' program
@ -1014,6 +977,9 @@ pub struct Args {
conflicts_with_all = ["command", "manual"], conflicts_with_all = ["command", "manual"],
)] )]
pub completions: Option<ShellCompletion>, pub completions: Option<ShellCompletion>,
#[command(flatten)]
pub logging: logging::LoggingArgs,
} }
#[derive(Clone, Copy, Debug, Default, ValueEnum)] #[derive(Clone, Copy, Debug, Default, ValueEnum)]
@ -1180,11 +1146,10 @@ fn expand_args_up_to_doubledash() -> Result<Vec<OsString>, std::io::Error> {
} }
#[inline] #[inline]
pub async fn get_args() -> Result<Args> { pub async fn get_args() -> Result<(Args, Option<WorkerGuard>)> {
use tracing::{debug, trace, warn}; let prearg_logs = logging::preargs();
if prearg_logs {
if std::env::var("RUST_LOG").is_ok() { warn!("⚠ RUST_LOG environment variable set or hardcoded, logging options have no effect");
warn!("⚠ RUST_LOG environment variable set, logging options have no effect");
} }
debug!("expanding @argfile arguments if any"); debug!("expanding @argfile arguments if any");
@ -1193,6 +1158,12 @@ pub async fn get_args() -> Result<Args> {
debug!("parsing arguments"); debug!("parsing arguments");
let mut args = Args::parse_from(args); let mut args = Args::parse_from(args);
let log_guard = if !prearg_logs {
logging::postargs(&args.logging).await?
} else {
None
};
// https://no-color.org/ // https://no-color.org/
if args.color == ColourMode::Auto && std::env::var("NO_COLOR").is_ok() { if args.color == ColourMode::Auto && std::env::var("NO_COLOR").is_ok() {
args.color = ColourMode::Never; args.color = ColourMode::Never;
@ -1213,10 +1184,12 @@ pub async fn get_args() -> Result<Args> {
} }
if args.no_environment { if args.no_environment {
warn!("--no-environment is deprecated");
args.emit_events_to = EmitEvents::None; args.emit_events_to = EmitEvents::None;
} }
if args.no_process_group { if args.no_process_group {
warn!("--no-process-group is deprecated");
args.wrap_process = WrapMode::None; args.wrap_process = WrapMode::None;
} }
@ -1242,19 +1215,22 @@ pub async fn get_args() -> Result<Args> {
.exit(); .exit();
} }
args.workdir = Some(if let Some(w) = take(&mut args.workdir) { let workdir = if let Some(w) = take(&mut args.workdir) {
w w
} else { } else {
let curdir = std::env::current_dir().into_diagnostic()?; let curdir = std::env::current_dir().into_diagnostic()?;
canonicalize(curdir).into_diagnostic()? canonicalize(curdir).into_diagnostic()?
}); };
debug!(workdir=?args.workdir, "current directory"); info!(path=?workdir, "effective working directory");
args.workdir = Some(workdir.clone());
let project_origin = if let Some(p) = take(&mut args.project_origin) { let project_origin = if let Some(p) = take(&mut args.project_origin) {
p p
} else { } else {
crate::dirs::project_origin(&args).await? crate::dirs::project_origin(&args).await?
}; };
info!(path=?project_origin, "effective project origin");
args.project_origin = Some(project_origin.clone());
args.paths = take(&mut args.recursive_paths) args.paths = take(&mut args.recursive_paths)
.into_iter() .into_iter()
@ -1288,14 +1264,13 @@ pub async fn get_args() -> Result<Args> {
.first() .first()
.map_or(false, |p| p.as_ref() == Path::new("/dev/null")) .map_or(false, |p| p.as_ref() == Path::new("/dev/null"))
{ {
debug!("only path is /dev/null, not watching anything"); info!("only path is /dev/null, not watching anything");
args.paths = Vec::new(); args.paths = Vec::new();
} else if args.paths.is_empty() { } else if args.paths.is_empty() {
debug!("no paths, using current directory"); info!("no paths, using current directory");
args.paths.push(args.workdir.clone().unwrap().into()); args.paths.push(args.workdir.clone().unwrap().into());
} }
info!(paths=?args.paths, "effective watched paths");
debug!(paths=?args.paths, "resolved all watched paths");
for (n, prog) in args.filter_programs.iter_mut().enumerate() { for (n, prog) in args.filter_programs.iter_mut().enumerate() {
if let Some(progpath) = prog.strip_prefix('@') { if let Some(progpath) = prog.strip_prefix('@') {
@ -1315,6 +1290,8 @@ pub async fn get_args() -> Result<Args> {
.map(parse_filter_program) .map(parse_filter_program)
.collect::<Result<_, _>>()?; .collect::<Result<_, _>>()?;
debug!(?args, "got arguments"); debug_assert!(args.workdir.is_some());
Ok(args) debug_assert!(args.project_origin.is_some());
info!(?args, "got arguments");
Ok((args, log_guard))
} }

View File

@ -0,0 +1,132 @@
use std::{env::var, io::stderr, path::PathBuf};
use clap::{ArgAction, Parser, ValueHint};
use miette::{bail, Result};
use tokio::fs::metadata;
use tracing::{info, warn};
use tracing_appender::{non_blocking, non_blocking::WorkerGuard, rolling};
#[derive(Debug, Clone, Parser)]
pub struct LoggingArgs {
/// Set diagnostic log level
///
/// This enables diagnostic logging, which is useful for investigating bugs or gaining more
/// insight into faulty filters or "missing" events. Use multiple times to increase verbosity.
///
/// Goes up to '-vvvv'. When submitting bug reports, default to a '-vvv' log level.
///
/// You may want to use with '--log-file' to avoid polluting your terminal.
///
/// Setting $RUST_LOG also works, and takes precedence, but is not recommended. However, using
/// $RUST_LOG is the only way to get logs from before these options are parsed.
#[arg(
long,
short,
help_heading = super::OPTSET_DEBUGGING,
action = ArgAction::Count,
default_value = "0",
num_args = 0,
)]
pub verbose: u8,
/// Write diagnostic logs to a file
///
/// This writes diagnostic logs to a file, instead of the terminal, in JSON format. If a log
/// level was not already specified, this will set it to '-vvv'.
///
/// If a path is not provided, the default is the working directory. Note that with
/// '--ignore-nothing', the write events to the log will likely get picked up by Watchexec,
/// causing a loop; prefer setting a path outside of the watched directory.
///
/// If the path provided is a directory, a file will be created in that directory. The file name
/// will be the current date and time, in the format 'watchexec.YYYY-MM-DDTHH-MM-SSZ.log'.
#[arg(
long,
help_heading = super::OPTSET_DEBUGGING,
num_args = 0..=1,
default_missing_value = ".",
value_hint = ValueHint::AnyPath,
value_name = "PATH",
)]
pub log_file: Option<PathBuf>,
}
pub fn preargs() -> bool {
let mut log_on = false;
#[cfg(feature = "dev-console")]
match console_subscriber::try_init() {
Ok(_) => {
warn!("dev-console enabled");
log_on = true;
}
Err(e) => {
eprintln!("Failed to initialise tokio console, falling back to normal logging\n{e}")
}
}
if !log_on && var("RUST_LOG").is_ok() {
match tracing_subscriber::fmt::try_init() {
Ok(()) => {
warn!(RUST_LOG=%var("RUST_LOG").unwrap(), "logging configured from RUST_LOG");
log_on = true;
}
Err(e) => eprintln!("Failed to initialise logging with RUST_LOG, falling back\n{e}"),
}
}
log_on
}
pub async fn postargs(args: &LoggingArgs) -> Result<Option<WorkerGuard>> {
if args.verbose == 0 {
return Ok(None);
}
let (log_writer, guard) = if let Some(file) = &args.log_file {
let is_dir = metadata(&file).await.map_or(false, |info| info.is_dir());
let (dir, filename) = if is_dir {
(
file.to_owned(),
PathBuf::from(format!(
"watchexec.{}.log",
chrono::Utc::now().format("%Y-%m-%dT%H-%M-%SZ")
)),
)
} else if let (Some(parent), Some(file_name)) = (file.parent(), file.file_name()) {
(parent.into(), PathBuf::from(file_name))
} else {
bail!("Failed to determine log file name");
};
non_blocking(rolling::never(dir, filename))
} else {
non_blocking(stderr())
};
let mut builder = tracing_subscriber::fmt().with_env_filter(match args.verbose {
0 => unreachable!("checked by if earlier"),
1 => "warn",
2 => "info",
3 => "debug",
_ => "trace",
});
if args.verbose > 2 {
use tracing_subscriber::fmt::format::FmtSpan;
builder = builder.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE);
}
match if args.log_file.is_some() {
builder.json().with_writer(log_writer).try_init()
} else if args.verbose > 3 {
builder.pretty().with_writer(log_writer).try_init()
} else {
builder.with_writer(log_writer).try_init()
} {
Ok(()) => info!("logging initialised"),
Err(e) => eprintln!("Failed to initialise logging, continuing with none\n{e}"),
}
Ok(Some(guard))
}

View File

@ -61,7 +61,7 @@ pub async fn project_origin(args: &Args) -> Result<PathBuf> {
.await .await
.into_diagnostic()? .into_diagnostic()?
}; };
info!(?project_origin, "resolved common/project origin"); debug!(?project_origin, "resolved common/project origin");
Ok(project_origin) Ok(project_origin)
} }
@ -72,7 +72,7 @@ pub async fn vcs_types(origin: &Path) -> Vec<ProjectType> {
.into_iter() .into_iter()
.filter(|pt| pt.is_vcs()) .filter(|pt| pt.is_vcs())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
info!(?vcs_types, "resolved vcs types"); info!(?vcs_types, "effective vcs types");
vcs_types vcs_types
} }

View File

@ -1,7 +1,7 @@
#![deny(rust_2018_idioms)] #![deny(rust_2018_idioms)]
#![allow(clippy::missing_const_for_fn, clippy::future_not_send)] #![allow(clippy::missing_const_for_fn, clippy::future_not_send)]
use std::{env::var, fs::File, io::Write, process::Stdio, sync::Mutex}; use std::{io::Write, process::Stdio};
use args::{Args, ShellCompletion}; use args::{Args, ShellCompletion};
use clap::CommandFactory; use clap::CommandFactory;
@ -9,8 +9,8 @@ use clap_complete::{Generator, Shell};
use clap_mangen::Man; use clap_mangen::Man;
use is_terminal::IsTerminal; use is_terminal::IsTerminal;
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result};
use tokio::{fs::metadata, io::AsyncWriteExt, process::Command}; use tokio::{io::AsyncWriteExt, process::Command};
use tracing::{debug, info, warn}; use tracing::{debug, info};
use watchexec::Watchexec; use watchexec::Watchexec;
use watchexec_events::{Event, Priority}; use watchexec_events::{Event, Priority};
@ -23,82 +23,6 @@ mod emits;
mod filterer; mod filterer;
mod state; mod state;
async fn init() -> Result<Args> {
let mut log_on = false;
#[cfg(feature = "dev-console")]
match console_subscriber::try_init() {
Ok(_) => {
warn!("dev-console enabled");
log_on = true;
}
Err(e) => {
eprintln!("Failed to initialise tokio console, falling back to normal logging\n{e}")
}
}
if !log_on && var("RUST_LOG").is_ok() {
match tracing_subscriber::fmt::try_init() {
Ok(()) => {
warn!(RUST_LOG=%var("RUST_LOG").unwrap(), "logging configured from RUST_LOG");
log_on = true;
}
Err(e) => eprintln!("Failed to initialise logging with RUST_LOG, falling back\n{e}"),
}
}
let args = args::get_args().await?;
let verbosity = args.verbose.unwrap_or(0);
if log_on {
warn!("ignoring logging options from args");
} else if verbosity > 0 {
let log_file = if let Some(file) = &args.log_file {
let is_dir = metadata(&file).await.map_or(false, |info| info.is_dir());
let path = if is_dir {
let filename = format!(
"watchexec.{}.log",
chrono::Utc::now().format("%Y-%m-%dT%H-%M-%SZ")
);
file.join(filename)
} else {
file.to_owned()
};
// TODO: use tracing-appender instead
Some(File::create(path).into_diagnostic()?)
} else {
None
};
let mut builder = tracing_subscriber::fmt().with_env_filter(match verbosity {
0 => unreachable!("checked by if earlier"),
1 => "warn",
2 => "info",
3 => "debug",
_ => "trace",
});
if verbosity > 2 {
use tracing_subscriber::fmt::format::FmtSpan;
builder = builder.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE);
}
match if let Some(writer) = log_file {
builder.json().with_writer(Mutex::new(writer)).try_init()
} else if verbosity > 3 {
builder.pretty().try_init()
} else {
builder.try_init()
} {
Ok(()) => info!("logging initialised"),
Err(e) => eprintln!("Failed to initialise logging, continuing with none\n{e}"),
}
}
Ok(args)
}
async fn run_watchexec(args: Args) -> Result<()> { async fn run_watchexec(args: Args) -> Result<()> {
info!(version=%env!("CARGO_PKG_VERSION"), "constructing Watchexec from CLI"); info!(version=%env!("CARGO_PKG_VERSION"), "constructing Watchexec from CLI");
@ -192,8 +116,7 @@ async fn run_completions(shell: ShellCompletion) -> Result<()> {
} }
pub async fn run() -> Result<()> { pub async fn run() -> Result<()> {
let args = init().await?; let (args, _log_guard) = args::get_args().await?;
debug!(?args, "arguments");
if args.manual { if args.manual {
run_manpage(args).await run_manpage(args).await

View File

@ -4,7 +4,7 @@
.SH NAME .SH NAME
watchexec \- Execute commands when watched files change watchexec \- Execute commands when watched files change
.SH SYNOPSIS .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\-\-map\-signal\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\-\-ignore\-nothing\fR] [\fB\-p\fR|\fB\-\-postpone\fR] [\fB\-\-delay\-run\fR] [\fB\-\-poll\fR] [\fB\-\-shell\fR] [\fB\-n \fR] [\fB\-\-emit\-events\-to\fR] [\fB\-\-only\-emit\-events\fR] [\fB\-E\fR|\fB\-\-env\fR] [\fB\-\-no\-process\-group\fR] [\fB\-\-wrap\-process\fR] [\fB\-N\fR|\fB\-\-notify\fR] [\fB\-\-color\fR] [\fB\-\-timings\fR] [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-\-bell\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\-j\fR|\fB\-\-filter\-prog\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\-W\fR|\fB\-\-watch\-non\-recursive\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\-\-map\-signal\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\-\-ignore\-nothing\fR] [\fB\-p\fR|\fB\-\-postpone\fR] [\fB\-\-delay\-run\fR] [\fB\-\-poll\fR] [\fB\-\-shell\fR] [\fB\-n \fR] [\fB\-\-emit\-events\-to\fR] [\fB\-\-only\-emit\-events\fR] [\fB\-E\fR|\fB\-\-env\fR] [\fB\-\-no\-process\-group\fR] [\fB\-\-wrap\-process\fR] [\fB\-N\fR|\fB\-\-notify\fR] [\fB\-\-color\fR] [\fB\-\-timings\fR] [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-\-bell\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\-j\fR|\fB\-\-filter\-prog\fR] [\fB\-i\fR|\fB\-\-ignore\fR] [\fB\-\-ignore\-file\fR] [\fB\-\-fs\-events\fR] [\fB\-\-no\-meta\fR] [\fB\-\-print\-events\fR] [\fB\-\-manual\fR] [\fB\-\-completions\fR] [\fB\-v\fR|\fB\-\-verbose\fR]... [\fB\-\-log\-file\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fICOMMAND\fR]
.SH DESCRIPTION .SH DESCRIPTION
Execute commands when watched files change. Execute commands when watched files change.
.PP .PP
@ -48,6 +48,13 @@ This option can be specified multiple times to watch multiple files or directori
The special value \*(Aq/dev/null\*(Aq, provided as the only path watched, will cause Watchexec to not watch any paths. Other event sources (like signals or key events) may still be used. The special value \*(Aq/dev/null\*(Aq, provided as the only path watched, will cause Watchexec to not watch any paths. Other event sources (like signals or key events) may still be used.
.TP .TP
\fB\-W\fR, \fB\-\-watch\-non\-recursive\fR=\fIPATH\fR
Watch a specific directory, non\-recursively
Unlike \*(Aq\-w\*(Aq, folders watched with this option are not recursed into.
This option can be specified multiple times to watch multiple directories non\-recursively.
.TP
\fB\-c\fR, \fB\-\-clear\fR=\fIMODE\fR \fB\-c\fR, \fB\-\-clear\fR=\fIMODE\fR
Clear screen before running command Clear screen before running command
@ -512,6 +519,18 @@ This prints the events that triggered the action when handling it (after debounc
Use \*(Aq\-v\*(Aq when you need more diagnostic information. Use \*(Aq\-v\*(Aq when you need more diagnostic information.
.TP .TP
\fB\-\-manual\fR
Show the manual page
This shows the manual page for Watchexec, if the output is a terminal and the \*(Aqman\*(Aq program is available. If not, the manual page is printed to stdout in ROFF format (suitable for writing to a watchexec.1 file).
.TP
\fB\-\-completions\fR=\fICOMPLETIONS\fR
Generate a shell completions script
Provides a completions script or configuration for the given shell. If Watchexec is not distributed with pre\-generated completions, you can use this to generate them yourself.
Supported shells: bash, elvish, fish, nu, powershell, zsh.
.TP
\fB\-v\fR, \fB\-\-verbose\fR \fB\-v\fR, \fB\-\-verbose\fR
Set diagnostic log level Set diagnostic log level
@ -532,18 +551,6 @@ If a path is not provided, the default is the working directory. Note that with
If the path provided is a directory, a file will be created in that directory. The file name will be the current date and time, in the format \*(Aqwatchexec.YYYY\-MM\-DDTHH\-MM\-SSZ.log\*(Aq. If the path provided is a directory, a file will be created in that directory. The file name will be the current date and time, in the format \*(Aqwatchexec.YYYY\-MM\-DDTHH\-MM\-SSZ.log\*(Aq.
.TP .TP
\fB\-\-manual\fR
Show the manual page
This shows the manual page for Watchexec, if the output is a terminal and the \*(Aqman\*(Aq program is available. If not, the manual page is printed to stdout in ROFF format (suitable for writing to a watchexec.1 file).
.TP
\fB\-\-completions\fR=\fICOMPLETIONS\fR
Generate a shell completions script
Provides a completions script or configuration for the given shell. If Watchexec is not distributed with pre\-generated completions, you can use this to generate them yourself.
Supported shells: bash, elvish, fish, nu, powershell, zsh.
.TP
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR
Print help (see a summary with \*(Aq\-h\*(Aq) Print help (see a summary with \*(Aq\-h\*(Aq)
.TP .TP

View File

@ -4,7 +4,8 @@ watchexec - Execute commands when watched files change
# SYNOPSIS # SYNOPSIS
**watchexec** \[**-w**\|**\--watch**\] \[**-c**\|**\--clear**\] **watchexec** \[**-w**\|**\--watch**\]
\[**-W**\|**\--watch-non-recursive**\] \[**-c**\|**\--clear**\]
\[**-o**\|**\--on-busy-update**\] \[**-r**\|**\--restart**\] \[**-o**\|**\--on-busy-update**\] \[**-r**\|**\--restart**\]
\[**-s**\|**\--signal**\] \[**\--stop-signal**\] \[**\--stop-timeout**\] \[**-s**\|**\--signal**\] \[**\--stop-signal**\] \[**\--stop-timeout**\]
\[**\--map-signal**\] \[**-d**\|**\--debounce**\] \[**\--stdin-quit**\] \[**\--map-signal**\] \[**-d**\|**\--debounce**\] \[**\--stdin-quit**\]
@ -20,10 +21,10 @@ watchexec - Execute commands when watched files change
\[**\--workdir**\] \[**-e**\|**\--exts**\] \[**-f**\|**\--filter**\] \[**\--workdir**\] \[**-e**\|**\--exts**\] \[**-f**\|**\--filter**\]
\[**\--filter-file**\] \[**-j**\|**\--filter-prog**\] \[**\--filter-file**\] \[**-j**\|**\--filter-prog**\]
\[**-i**\|**\--ignore**\] \[**\--ignore-file**\] \[**\--fs-events**\] \[**-i**\|**\--ignore**\] \[**\--ignore-file**\] \[**\--fs-events**\]
\[**\--no-meta**\] \[**\--print-events**\] \[**\--no-meta**\] \[**\--print-events**\] \[**\--manual**\]
\[**-v**\|**\--verbose**\]\... \[**\--log-file**\] \[**\--manual**\] \[**\--completions**\] \[**-v**\|**\--verbose**\]\...
\[**\--completions**\] \[**-h**\|**\--help**\] \[**\--log-file**\] \[**-h**\|**\--help**\] \[**-V**\|**\--version**\]
\[**-V**\|**\--version**\] \[*COMMAND*\] \[*COMMAND*\]
# DESCRIPTION # DESCRIPTION
@ -82,6 +83,15 @@ The special value /dev/null, provided as the only path watched, will
cause Watchexec to not watch any paths. Other event sources (like cause Watchexec to not watch any paths. Other event sources (like
signals or key events) may still be used. signals or key events) may still be used.
**-W**, **\--watch-non-recursive**=*PATH*
: Watch a specific directory, non-recursively
Unlike -w, folders watched with this option are not recursed into.
This option can be specified multiple times to watch multiple
directories non-recursively.
**-c**, **\--clear**=*MODE* **-c**, **\--clear**=*MODE*
: Clear screen before running command : Clear screen before running command
@ -748,6 +758,24 @@ filters.
Use -v when you need more diagnostic information. Use -v when you need more diagnostic information.
**\--manual**
: Show the manual page
This shows the manual page for Watchexec, if the output is a terminal
and the man program is available. If not, the manual page is printed to
stdout in ROFF format (suitable for writing to a watchexec.1 file).
**\--completions**=*COMPLETIONS*
: Generate a shell completions script
Provides a completions script or configuration for the given shell. If
Watchexec is not distributed with pre-generated completions, you can use
this to generate them yourself.
Supported shells: bash, elvish, fish, nu, powershell, zsh.
**-v**, **\--verbose** **-v**, **\--verbose**
: Set diagnostic log level : Set diagnostic log level
@ -782,24 +810,6 @@ If the path provided is a directory, a file will be created in that
directory. The file name will be the current date and time, in the directory. The file name will be the current date and time, in the
format watchexec.YYYY-MM-DDTHH-MM-SSZ.log. format watchexec.YYYY-MM-DDTHH-MM-SSZ.log.
**\--manual**
: Show the manual page
This shows the manual page for Watchexec, if the output is a terminal
and the man program is available. If not, the manual page is printed to
stdout in ROFF format (suitable for writing to a watchexec.1 file).
**\--completions**=*COMPLETIONS*
: Generate a shell completions script
Provides a completions script or configuration for the given shell. If
Watchexec is not distributed with pre-generated completions, you can use
this to generate them yourself.
Supported shells: bash, elvish, fish, nu, powershell, zsh.
**-h**, **\--help** **-h**, **\--help**
: Print help (see a summary with -h) : Print help (see a summary with -h)