zoxide/src/subcommand/init.rs

322 lines
6.2 KiB
Rust

use anyhow::{bail, Result};
use clap::arg_enum;
use structopt::StructOpt;
use std::io::{self, Write};
#[derive(Debug, StructOpt)]
#[structopt(about = "Generates shell configuration")]
pub struct Init {
#[structopt(possible_values = &Shell::variants(), case_insensitive = true)]
shell: Shell,
#[structopt(
long,
help = "Changes the name of the 'z' command",
default_value = "z"
)]
z_cmd: String,
#[structopt(
long,
help = "Prevents zoxide from defining any commands other than 'z'"
)]
no_define_aliases: bool,
#[structopt(
long,
help = "Chooses event on which an entry is added to the database",
possible_values = &Hook::variants(),
default_value = "pwd",
case_insensitive = true
)]
hook: Hook,
}
impl Init {
pub fn run(&self) -> Result<()> {
let config = match self.shell {
Shell::bash => BASH_CONFIG,
Shell::fish => FISH_CONFIG,
Shell::posix => POSIX_CONFIG,
Shell::zsh => ZSH_CONFIG,
};
let stdout = io::stdout();
let mut handle = stdout.lock();
let z = config.z;
writeln!(handle, "{}", z(&self.z_cmd)).unwrap();
if !self.no_define_aliases {
let alias = config.alias;
writeln!(handle, "{}", alias(&self.z_cmd)).unwrap();
}
match self.hook {
Hook::none => (),
Hook::prompt => writeln!(handle, "{}", config.hook.prompt).unwrap(),
Hook::pwd => match config.hook.pwd {
Some(pwd_hook) => writeln!(handle, "{}", pwd_hook).unwrap(),
None => bail!("PWD hooks are currently unsupported on this shell. Use `--hook prompt` to change this."),
},
}
Ok(())
}
}
arg_enum! {
#[allow(non_camel_case_types)]
#[derive(Debug)]
enum Shell {
bash,
fish,
posix,
zsh,
}
}
arg_enum! {
#[allow(non_camel_case_types)]
#[derive(Debug)]
enum Hook {
none,
prompt,
pwd,
}
}
const BASH_CONFIG: ShellConfig = ShellConfig {
z: bash_z,
alias: bash_alias,
hook: HookConfig {
prompt: BASH_HOOK_PROMPT,
pwd: Some(BASH_HOOK_PWD),
},
};
const FISH_CONFIG: ShellConfig = ShellConfig {
z: fish_z,
alias: fish_alias,
hook: HookConfig {
prompt: FISH_HOOK_PROMPT,
pwd: Some(FISH_HOOK_PWD),
},
};
const POSIX_CONFIG: ShellConfig = ShellConfig {
z: posix_z,
alias: posix_alias,
hook: HookConfig {
prompt: POSIX_HOOK_PROMPT,
pwd: None,
},
};
const ZSH_CONFIG: ShellConfig = ShellConfig {
z: zsh_z,
alias: zsh_alias,
hook: HookConfig {
prompt: ZSH_HOOK_PROMPT,
pwd: Some(ZSH_HOOK_PWD),
},
};
struct ShellConfig {
z: fn(&str) -> String,
alias: fn(&str) -> String,
hook: HookConfig,
}
struct HookConfig {
prompt: &'static str,
pwd: Option<&'static str>,
}
fn fish_z(z_cmd: &str) -> String {
format!(
r#"
function _z_cd
cd $argv
or return $status
commandline -f repaint
if test -n "$_ZO_ECHO"
echo $PWD
end
end
function {}
set argc (count $argv)
if test $argc -eq 0
_z_cd $HOME
or return $status
else if test $argc -eq 1 -a $argv[1] = '-'
_z_cd -
or return $status
else
# FIXME: use string-collect from fish 3.1.0 once it has wider adoption
set -l IFS ''
set -l result (zoxide query $argv)
if test -d $result; and string length -q -- $result
_z_cd $result
or return $status
else if test -n "$result"
echo $result
end
end
end
"#,
z_cmd
)
}
fn posix_z(z_cmd: &str) -> String {
format!(
r#"
_z_cd() {{
cd "$@" || return "$?"
if [ -n "$_ZO_ECHO" ]; then
echo "$PWD"
fi
}}
{}() {{
if [ "$#" -eq 0 ]; then
_z_cd ~ || return "$?"
elif [ "$#" -eq 1 ] && [ "$1" = '-' ]; then
if [ -n "$OLDPWD" ]; then
_z_cd "$OLDPWD" || return "$?"
else
echo 'zoxide: $OLDPWD is not set'
return 1
fi
else
result="$(zoxide query "$@")" || return "$?"
if [ -d "$result" ]; then
_z_cd "$result" || return "$?"
elif [ -n "$result" ]; then
echo "$result"
fi
fi
}}
"#,
z_cmd
)
}
use posix_z as bash_z;
use posix_z as zsh_z;
fn fish_alias(z_cmd: &str) -> String {
format!(
r#"
abbr -a zi '{} -i'
abbr -a za 'zoxide add'
abbr -a zq 'zoxide query'
abbr -a zqi 'zoxide query -i'
abbr -a zr 'zoxide remove'
abbr -a zri 'zoxide remove -i'
"#,
z_cmd
)
}
fn posix_alias(z_cmd: &str) -> String {
format!(
r#"
alias zi='{} -i'
alias za='zoxide add'
alias zq='zoxide query'
alias zqi='zoxide query -i'
alias zr='zoxide remove'
alias zri='zoxide remove -i'
"#,
z_cmd
)
}
use posix_alias as bash_alias;
use posix_alias as zsh_alias;
const BASH_HOOK_PROMPT: &str = r#"
_zoxide_hook() {
zoxide add
}
case "$PROMPT_COMMAND" in
*_zoxide_hook*) ;;
*) PROMPT_COMMAND="_zoxide_hook${PROMPT_COMMAND:+;${PROMPT_COMMAND}}" ;;
esac
"#;
const FISH_HOOK_PROMPT: &str = r#"
function _zoxide_hook --on-event fish_prompt
zoxide add
end
"#;
const POSIX_HOOK_PROMPT: &str = r#"
_zoxide_hook() {
zoxide add > /dev/null
}
case "$PS1" in
*\$\(_zoxide_hook\)*) ;;
*) PS1="\$(_zoxide_hook)${PS1}" ;;
esac
"#;
const ZSH_HOOK_PROMPT: &str = r#"
_zoxide_hook() {
zoxide add
}
[[ -n "${precmd_functions[(r)_zoxide_hook]}" ]] || {
precmd_functions+=(_zoxide_hook)
}
"#;
const BASH_HOOK_PWD: &str = r#"
_zoxide_hook() {
if [ -z "${_ZO_PWD}" ]; then
_ZO_PWD="${PWD}"
elif [ "${_ZO_PWD}" != "${PWD}" ]; then
_ZO_PWD="${PWD}"
zoxide add
fi
}
case "$PROMPT_COMMAND" in
*_zoxide_hook*) ;;
*) PROMPT_COMMAND="_zoxide_hook${PROMPT_COMMAND:+;${PROMPT_COMMAND}}" ;;
esac
"#;
const FISH_HOOK_PWD: &str = r#"
function _zoxide_hook --on-variable PWD
zoxide add
end
"#;
const ZSH_HOOK_PWD: &str = r#"
_zoxide_hook() {
zoxide add
}
chpwd_functions=(${chpwd_functions[@]} "_zoxide_hook")
"#;