Skip to content

Commit

Permalink
Merge pull request #5647 from epage/infer
Browse files Browse the repository at this point in the history
feat(complete): Infer shell for dynamic completions
  • Loading branch information
epage authored Aug 9, 2024
2 parents 8a1f9b3 + e65e463 commit efdcad1
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 14 deletions.
85 changes: 74 additions & 11 deletions clap_complete/src/dynamic/shells/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::path::Path>) -> Option<Shell> {
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<Shell> {
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<Shell> {
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 {
Expand Down Expand Up @@ -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<PossibleValue> {
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"),
})
}
}
Expand All @@ -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,
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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\')'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'\'')]' \
Expand Down

0 comments on commit efdcad1

Please sign in to comment.