diff --git a/clap_complete/src/dynamic/shells/shell.rs b/clap_complete/src/dynamic/shells/shell.rs index 874589f8c7d..7b2598ba965 100644 --- a/clap_complete/src/dynamic/shells/shell.rs +++ b/clap_complete/src/dynamic/shells/shell.rs @@ -10,14 +10,77 @@ use clap::ValueEnum; pub enum Shell { /// Bourne Again `SHell` (bash) Bash, + /// Elvish shell + Elvish, /// Friendly Interactive `SHell` (fish) Fish, - /// Z shell (zsh) - Zsh, - /// Elf `SHell` (elvish) - Elvish, - /// Powerful `SHell` (powershel) + /// `PowerShell` Powershell, + /// Z `SHell` (zsh) + Zsh, +} + +impl Shell { + /// Parse a shell from a path to the executable for the shell + /// + /// # Examples + /// + /// ``` + /// use clap_complete::shells::Shell; + /// + /// assert_eq!(Shell::from_shell_path("/bin/bash"), Some(Shell::Bash)); + /// assert_eq!(Shell::from_shell_path("/usr/bin/zsh"), Some(Shell::Zsh)); + /// assert_eq!(Shell::from_shell_path("/opt/my_custom_shell"), None); + /// ``` + pub fn from_shell_path(path: impl AsRef) -> Option { + parse_shell_from_path(path.as_ref()) + } + + /// Determine the user's current shell from the environment + /// + /// This will read the SHELL environment variable and try to determine which shell is in use + /// from that. + /// + /// If SHELL is not set, then on windows, it will default to powershell, and on + /// other operating systems it will return `None`. + /// + /// If SHELL is set, but contains a value that doesn't correspond to one of the supported shell + /// types, then return `None`. + /// + /// # Example: + /// + /// ```no_run + /// # use clap::Command; + /// use clap_complete::{generate, shells::Shell}; + /// # fn build_cli() -> Command { + /// # Command::new("compl") + /// # } + /// let shell = Shell::from_env(); + /// println!("{shell:?}"); + /// ``` + pub fn from_env() -> Option { + if let Some(env_shell) = std::env::var_os("SHELL") { + Shell::from_shell_path(env_shell) + } else if cfg!(windows) { + Some(Shell::Powershell) + } else { + None + } + } +} + +// use a separate function to avoid having to monomorphize the entire function due +// to from_shell_path being generic +fn parse_shell_from_path(path: &std::path::Path) -> Option { + let name = path.file_stem()?.to_str()?; + match name { + "bash" => Some(Shell::Bash), + "elvish" => Some(Shell::Elvish), + "fish" => Some(Shell::Fish), + "powershell" | "powershell_ise" => Some(Shell::Powershell), + "zsh" => Some(Shell::Zsh), + _ => None, + } } impl Display for Shell { @@ -47,20 +110,20 @@ impl ValueEnum for Shell { fn value_variants<'a>() -> &'a [Self] { &[ Shell::Bash, - Shell::Fish, - Shell::Zsh, Shell::Elvish, + Shell::Fish, Shell::Powershell, + Shell::Zsh, ] } fn to_possible_value(&self) -> Option { Some(match self { Shell::Bash => PossibleValue::new("bash"), - Shell::Fish => PossibleValue::new("fish"), - Shell::Zsh => PossibleValue::new("zsh"), Shell::Elvish => PossibleValue::new("elvish"), + Shell::Fish => PossibleValue::new("fish"), Shell::Powershell => PossibleValue::new("powershell"), + Shell::Zsh => PossibleValue::new("zsh"), }) } } @@ -69,10 +132,10 @@ impl Shell { fn completer(&self) -> &dyn crate::dynamic::shells::ShellCompleter { match self { Self::Bash => &super::Bash, - Self::Fish => &super::Fish, - Self::Zsh => &super::Zsh, Self::Elvish => &super::Elvish, + Self::Fish => &super::Fish, Self::Powershell => &super::Powershell, + Self::Zsh => &super::Zsh, } } } diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc index e6dde35122c..49c9960ba11 100644 --- a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc +++ b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc @@ -245,7 +245,7 @@ _exhaustive() { fi case "${prev}" in --shell) - COMPREPLY=($(compgen -W "bash fish zsh elvish powershell" -- "${cur}")) + COMPREPLY=($(compgen -W "bash elvish fish powershell zsh" -- "${cur}")) return 0 ;; --register) diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish b/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish index 1b4b9bd7910..7b28a7cd611 100644 --- a/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish +++ b/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish @@ -136,7 +136,7 @@ complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -l email -r complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -l global -d 'everywhere' complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -s h -l help -d 'Print help' complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -s V -l version -d 'Print version' -complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -l shell -d 'Specify shell to complete for' -r -f -a "{bash\t'',fish\t'',zsh\t'',elvish\t'',powershell\t''}" +complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -l shell -d 'Specify shell to complete for' -r -f -a "{bash\t'',elvish\t'',fish\t'',powershell\t'',zsh\t''}" complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -l register -d 'Path to write completion-registration to' -r -F complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -l global -d 'everywhere' complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -s h -l help -d 'Print help (see more with \'--help\')' diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive b/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive index 9683cac4d00..d596f71e25a 100644 --- a/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive +++ b/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive @@ -325,7 +325,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (complete) _arguments "${_arguments_options[@]}" : \ -'--shell=[Specify shell to complete for]:SHELL:(bash fish zsh elvish powershell)' \ +'--shell=[Specify shell to complete for]:SHELL:(bash elvish fish powershell zsh)' \ '--register=[Path to write completion-registration to]:REGISTER:_files' \ '--global[everywhere]' \ '-h[Print help (see more with '\''--help'\'')]' \