From 317221584a12711a9e1c3e51c424cdb743951ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Saparelli?= Date: Sat, 20 Apr 2024 22:58:29 +1200 Subject: [PATCH] Breaking changes for --shell (#817) --- completions/bash | 2 +- completions/elvish | 1 - completions/fish | 1 - completions/nu | 1 - completions/powershell | 1 - completions/zsh | 1 - crates/cli/src/args.rs | 25 ++++---------- crates/cli/src/config.rs | 71 +++++++++++++++++++++++++--------------- doc/watchexec.1 | 8 ++--- doc/watchexec.1.md | 15 ++++----- 10 files changed, 63 insertions(+), 63 deletions(-) diff --git a/completions/bash b/completions/bash index d4447c3..197d95c 100644 --- a/completions/bash +++ b/completions/bash @@ -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 -j -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 --filter-prog --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 -j -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-environment --emit-events-to --only-emit-events --env --no-process-group --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]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 diff --git a/completions/elvish b/completions/elvish index b948a0a..109f8a7 100644 --- a/completions/elvish +++ b/completions/elvish @@ -69,7 +69,6 @@ set edit:completion:arg-completer[watchexec] = {|@words| cand -p 'Wait until first change before running command' cand --postpone 'Wait until first change before running command' 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' diff --git a/completions/fish b/completions/fish index 7bcbe09..dcf96d9 100644 --- a/completions/fish +++ b/completions/fish @@ -35,7 +35,6 @@ complete -c watchexec -l no-discover-ignore -d 'Don\'t discover ignore files at complete -c watchexec -l ignore-nothing -d 'Don\'t ignore anything at all' complete -c watchexec -s p -l postpone -d 'Wait until first change before running command' 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' diff --git a/completions/nu b/completions/nu index 6bf12e9..98b65ea 100644 --- a/completions/nu +++ b/completions/nu @@ -50,7 +50,6 @@ module completions { --poll: string # Poll for filesystem changes --shell: string # Use a different shell -n # Don't use a shell - --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 diff --git a/completions/powershell b/completions/powershell index 8673633..9772f95 100644 --- a/completions/powershell +++ b/completions/powershell @@ -72,7 +72,6 @@ Register-ArgumentCompleter -Native -CommandName 'watchexec' -ScriptBlock { [CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Wait until first change before running command') [CompletionResult]::new('--postpone', 'postpone', [CompletionResultType]::ParameterName, 'Wait until first change before running command') [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') diff --git a/completions/zsh b/completions/zsh index a73496f..b8b0a79 100644 --- a/completions/zsh +++ b/completions/zsh @@ -66,7 +66,6 @@ _watchexec() { '-p[Wait until first change before running command]' \ '--postpone[Wait until first change before running command]' \ '-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]' \ diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index 76e2f9e..abbdb86 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -438,18 +438,18 @@ pub struct Args { /// Use a different shell /// - /// By default, Watchexec will use 'sh' on unix and 'cmd' (CMD.EXE) on Windows. With this, you - /// can override that and use a different shell, for example one with more features or one which - /// has your custom aliases and functions. + /// By default, Watchexec will use '$SHELL' if it's defined or a default of 'sh' on Unix-likes, + /// and either 'pwsh', 'powershell', or 'cmd' (CMD.EXE) on Windows, depending on what Watchexec + /// detects is the running shell. + /// + /// With this option, you can override that and use a different shell, for example one with more + /// features or one which has your custom aliases and functions. /// /// If the value has spaces, it is parsed as a command line, and the first word used as the /// shell program, with the rest as arguments to the shell. /// /// The command is run with the '-c' flag (except for 'cmd' on Windows, where it's '/C'). /// - /// Note that the default shell will change at the next major release: the value of '$SHELL' - /// will be respected, falling back to 'sh' on unix and to PowerShell on Windows. - /// /// The special value 'none' can be used to disable shell use entirely. In that case, the /// command provided to Watchexec will be parsed, with the first word being the executable and /// the rest being the arguments, and executed directly. Note that this parsing is rudimentary, @@ -469,7 +469,7 @@ pub struct Args { /// /// $ watchexec --shell=pwsh -- Test-Connection localhost /// - /// Use with cmd (default on Windows): + /// Use with CMD.exe: /// /// $ watchexec --shell=cmd -- dir /// @@ -496,17 +496,6 @@ pub struct Args { )] pub no_shell: bool, - /// Don't use a shell - /// - /// This is a deprecated alias for '--shell=none'. - #[arg( - long, - hide = true, - help_heading = OPTSET_COMMAND, - alias = "no-shell", // deprecated - )] - pub no_shell_long: bool, - /// Shorthand for '--emit-events=none' /// /// This is the old way to disable event emission into the environment. See '--emit-events' for diff --git a/crates/cli/src/config.rs b/crates/cli/src/config.rs index 87aaf15..a018b53 100644 --- a/crates/cli/src/config.rs +++ b/crates/cli/src/config.rs @@ -1,7 +1,7 @@ use std::{ borrow::Cow, collections::HashMap, - env::current_dir, + env::{current_dir, var}, ffi::{OsStr, OsString}, fs::File, io::{IsTerminal, Write}, @@ -469,35 +469,52 @@ fn interpret_command_args(args: &Args) -> Result> { panic!("(clap) Bug: command is not present"); } - let shell = match if args.no_shell || args.no_shell_long { + let shell = if args.no_shell { None } else { - args.shell.as_deref().or(Some("default")) - } { - Some("") => return Err(RuntimeError::CommandShellEmptyShell).into_diagnostic(), - - Some("none") | None => None, - - #[cfg(windows)] - Some("default") | Some("cmd") | Some("cmd.exe") | Some("CMD") | Some("CMD.EXE") => { - Some(Shell::cmd()) - } - - #[cfg(not(windows))] - Some("default") => Some(Shell::new("sh")), - - Some(other) => { - let sh = other.split_ascii_whitespace().collect::>(); - - // UNWRAP: checked by Some("") - #[allow(clippy::unwrap_used)] - let (shprog, shopts) = sh.split_first().unwrap(); - - Some(Shell { - prog: shprog.into(), - options: shopts.iter().map(|s| (*s).to_string()).collect(), - program_option: Some(Cow::Borrowed(OsStr::new("-c"))), + let shell = args.shell.clone().or_else(|| var("SHELL").ok()); + match shell + .as_deref() + .or_else(|| { + if cfg!(not(windows)) { + Some("sh") + } else if var("POWERSHELL_DISTRIBUTION_CHANNEL").is_ok() + && (which::which("pwsh").is_ok() || which::which("pwsh.exe").is_ok()) + { + trace!("detected pwsh"); + Some("pwsh") + } else if var("PSModulePath").is_ok() + && (which::which("powershell").is_ok() + || which::which("powershell.exe").is_ok()) + { + trace!("detected powershell"); + Some("powershell") + } else { + Some("cmd") + } }) + .or(Some("default")) + { + Some("") => return Err(RuntimeError::CommandShellEmptyShell).into_diagnostic(), + + Some("none") | None => None, + + #[cfg(windows)] + Some("cmd") | Some("cmd.exe") | Some("CMD") | Some("CMD.EXE") => Some(Shell::cmd()), + + Some(other) => { + let sh = other.split_ascii_whitespace().collect::>(); + + // UNWRAP: checked by Some("") + #[allow(clippy::unwrap_used)] + let (shprog, shopts) = sh.split_first().unwrap(); + + Some(Shell { + prog: shprog.into(), + options: shopts.iter().map(|s| (*s).to_string()).collect(), + program_option: Some(Cow::Borrowed(OsStr::new("-c"))), + }) + } } }; diff --git a/doc/watchexec.1 b/doc/watchexec.1 index 4a204e4..4a0fdbb 100644 --- a/doc/watchexec.1 +++ b/doc/watchexec.1 @@ -216,14 +216,14 @@ Aliased as \*(Aq\-\-force\-poll\*(Aq. \fB\-\-shell\fR=\fISHELL\fR Use a different shell -By default, Watchexec will use \*(Aqsh\*(Aq on unix and \*(Aqcmd\*(Aq (CMD.EXE) on Windows. With this, you can override that and use a different shell, for example one with more features or one which has your custom aliases and functions. +By default, Watchexec will use \*(Aq$SHELL\*(Aq if it\*(Aqs defined or a default of \*(Aqsh\*(Aq on Unix\-likes, and either \*(Aqpwsh\*(Aq, \*(Aqpowershell\*(Aq, or \*(Aqcmd\*(Aq (CMD.EXE) on Windows, depending on what Watchexec detects is the running shell. + +With this option, you can override that and use a different shell, for example one with more features or one which has your custom aliases and functions. If the value has spaces, it is parsed as a command line, and the first word used as the shell program, with the rest as arguments to the shell. The command is run with the \*(Aq\-c\*(Aq flag (except for \*(Aqcmd\*(Aq on Windows, where it\*(Aqs \*(Aq/C\*(Aq). -Note that the default shell will change at the next major release: the value of \*(Aq$SHELL\*(Aq will be respected, falling back to \*(Aqsh\*(Aq on unix and to PowerShell on Windows. - The special value \*(Aqnone\*(Aq can be used to disable shell use entirely. In that case, the command provided to Watchexec will be parsed, with the first word being the executable and the rest being the arguments, and executed directly. Note that this parsing is rudimentary, and may not work as expected in all cases. Using \*(Aqnone\*(Aq is a little more efficient and can enable a stricter interpretation of the input, but it also means that you can\*(Aqt use shell features like globbing, redirection, control flow, logic, or pipes. @@ -238,7 +238,7 @@ Use with powershell core: $ watchexec \-\-shell=pwsh \-\- Test\-Connection localhost -Use with cmd (default on Windows): +Use with CMD.exe: $ watchexec \-\-shell=cmd \-\- dir diff --git a/doc/watchexec.1.md b/doc/watchexec.1.md index b8b1853..3e14349 100644 --- a/doc/watchexec.1.md +++ b/doc/watchexec.1.md @@ -348,9 +348,12 @@ Aliased as \--force-poll. : Use a different shell -By default, Watchexec will use sh on unix and cmd (CMD.EXE) on Windows. -With this, you can override that and use a different shell, for example -one with more features or one which has your custom aliases and +By default, Watchexec will use \$SHELL if its defined or a default of sh +on Unix-likes, and either pwsh, powershell, or cmd (CMD.EXE) on Windows, +depending on what Watchexec detects is the running shell. + +With this option, you can override that and use a different shell, for +example one with more features or one which has your custom aliases and functions. If the value has spaces, it is parsed as a command line, and the first @@ -359,10 +362,6 @@ word used as the shell program, with the rest as arguments to the shell. The command is run with the -c flag (except for cmd on Windows, where its /C). -Note that the default shell will change at the next major release: the -value of \$SHELL will be respected, falling back to sh on unix and to -PowerShell on Windows. - The special value none can be used to disable shell use entirely. In that case, the command provided to Watchexec will be parsed, with the first word being the executable and the rest being the arguments, and @@ -383,7 +382,7 @@ Use with powershell core: \$ watchexec \--shell=pwsh \-- Test-Connection localhost -Use with cmd (default on Windows): +Use with CMD.exe: \$ watchexec \--shell=cmd \-- dir