Rewrite init commands

This commit is contained in:
Ajeet D'Souza 2020-08-28 01:34:05 +05:30
parent 2add9de830
commit f4525db02f
14 changed files with 991 additions and 455 deletions

View File

@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Added
- `zoxide init` now defines `__zoxide_z*` functions that can be aliased as needed.
### Changed
- `zoxide init --no-aliases` no longer generates `z` or `zi`.
## [0.4.3] - 2020-07-04
### Fixed

View File

@ -53,6 +53,13 @@ pub fn zo_maxage() -> Result<Rank> {
}
}
pub fn zo_echo() -> bool {
match env::var_os("_ZO_ECHO") {
Some(var) => var == "1",
None => false,
}
}
pub fn zo_resolve_symlinks() -> bool {
match env::var_os("_ZO_RESOLVE_SYMLINKS") {
Some(var) => var == "1",

182
src/subcommand/init/bash.rs Normal file
View File

@ -0,0 +1,182 @@
use anyhow::Result;
use std::io::Write;
use super::{Hook, Init};
use crate::config;
pub fn run<W: Write>(writer: &mut W, options: &Init) -> Result<()> {
const NOT_CONFIGURED: &str = "\
# -- not configured --";
let __zoxide_pwd = if config::zo_resolve_symlinks() {
"\
__zoxide_pwd() {
pwd -P
}"
} else {
"\
__zoxide_pwd() {
pwd -L
}"
};
let __zoxide_cd = if config::zo_echo() {
"\
__zoxide_cd() {
cd \"$@\" || return \"$?\"
__zoxide_pwd
}"
} else {
"\
__zoxide_cd() {
cd \"$@\" || return \"$?\"
}"
};
let __zoxide_hook = match options.hook {
Hook::none => NOT_CONFIGURED,
Hook::prompt => {
"\
__zoxide_hook() {
zoxide add \"$(__zoxide_pwd)\"
}"
}
Hook::pwd => {
"\
__zoxide_hook() {
local -r __zoxide_pwd_tmp=\"$(__zoxide_pwd)\"
if [ -z \"$__zoxide_pwd_old\" ]; then
__zoxide_pwd_old=\"$__zoxide_pwd_tmp\"
elif [ \"$__zoxide_pwd_old\" != \"$__zoxide_pwd_tmp\" ]; then
__zoxide_pwd_old=\"$__zoxide_pwd_tmp\"
zoxide add \"$__zoxide_pwd_old\"
fi
}"
}
};
let hook_init = match options.hook {
Hook::none => NOT_CONFIGURED,
_ => {
"\
case \"$PROMPT_COMMAND\" in
*__zoxide_hook*) ;;
*) PROMPT_COMMAND=\"${PROMPT_COMMAND:+${PROMPT_COMMAND};}__zoxide_hook\" ;;
esac"
}
};
let aliases = if options.no_aliases {
NOT_CONFIGURED.into()
} else {
format!(
"\
alias {}='__zoxide_z'
alias {cmd}i='__zoxide_zi'
alias {cmd}a='__zoxide_za'
alias {cmd}q='__zoxide_zq'
alias {cmd}qi='__zoxide_zqi'
alias {cmd}r='__zoxide_zr'
alias {cmd}ri='__zoxide_zri'",
cmd = options.cmd
)
};
write!(
writer,
"\
# =============================================================================
#
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
{__zoxide_pwd}
# cd + custom logic based on the value of _ZO_ECHO.
{__zoxide_cd}
# =============================================================================
#
# Hook configuration for zoxide.
#
# Hook to add new entries to the database.
{__zoxide_hook}
# Initialize hook.
{hook_init}
# =============================================================================
#
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
__zoxide_z() {{
if [ \"$#\" -eq 0 ]; then
__zoxide_cd ~
elif [ \"$#\" -eq 1 ] && [ \"$1\" = '-' ]; then
if [ -n \"$OLDPWD\" ]; then
__zoxide_cd \"$OLDPWD\"
else
echo \"zoxide: \\$OLDPWD is not set\"
return 1
fi
else
local __zoxide_result
__zoxide_result=\"$(zoxide query -- \"$@\")\" && __zoxide_cd \"$__zoxide_result\"
fi
}}
# Jump to a directory using interactive search.
__zoxide_zi() {{
local __zoxide_result
__zoxide_result=\"$(zoxide query -i -- \"$@\")\" && __zoxide_cd \"$__zoxide_result\"
}}
# Add a new entry to the database.
alias __zoxide_za='zoxide add'
# Query an entry from the database using only keywords.
alias __zoxide_zq='zoxide query'
# Query an entry from the database using interactive selection.
alias __zoxide_zqi='zoxide query -i'
# Remove an entry from the database using the exact path.
alias __zoxide_zr='zoxide remove'
# Remove an entry from the database using interactive selection.
__zoxide_zri() {{
local __zoxide_result
__zoxide_result=\"$(zoxide query -i -- \"$@\")\" && zoxide remove \"$__zoxide_result\"
}}
# =============================================================================
#
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{aliases}
# =============================================================================
#
# To initialize zoxide with bash, add the following line to your bash
# configuration file (usually ~/.bashrc):
#
# eval \"$(zoxide init bash)\"
",
__zoxide_pwd = __zoxide_pwd,
__zoxide_cd = __zoxide_cd,
__zoxide_hook = __zoxide_hook,
hook_init = hook_init,
aliases = aliases,
)?;
Ok(())
}

191
src/subcommand/init/fish.rs Normal file
View File

@ -0,0 +1,191 @@
use anyhow::Result;
use std::io::Write;
use super::{Hook, Init};
use crate::config;
pub fn run<W: Write>(writer: &mut W, options: &Init) -> Result<()> {
const NOT_CONFIGURED: &str = "\
# -- not configured --";
let __zoxide_pwd = if config::zo_resolve_symlinks() {
"\
function __zoxide_pwd
pwd -P
end"
} else {
"\
function __zoxide_pwd
pwd -L
end"
};
let __zoxide_cd = if config::zo_echo() {
"\
function __zoxide_cd
cd $argv
or return $status
commandline -f repaint
__zoxide_pwd
end"
} else {
"\
function __zoxide_cd
cd $argv
or return $status
commandline -f repaint
end"
};
let __zoxide_hook = "\
function __zoxide_hook
zoxide add (__zoxide_pwd)
end";
let hook_init = match options.hook {
Hook::none => NOT_CONFIGURED,
Hook::prompt => {
"\
function __zoxide_hook_prompt --on-event fish_prompt
__zoxide_hook
end"
}
Hook::pwd => {
"\
function __zoxide_hook_pwd --on-variable PWD
__zoxide_hook
end"
}
};
let aliases = if options.no_aliases {
NOT_CONFIGURED.into()
} else {
format!(
"\
function {cmd}
__zoxide_z $argv
end
function {cmd}i
__zoxide_zi $argv
end
function {cmd}a
__zoxide_za $argv
end
function {cmd}q
__zoxide_zq $argv
end
function {cmd}qi
__zoxide_zqi $argv
end
function {cmd}r
__zoxide_zr $argv
end
function {cmd}ri
__zoxide_zri $argv
end",
cmd = options.cmd
)
};
writeln!(
writer,
"\
# =============================================================================
#
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
{__zoxide_pwd}
# cd + custom logic based on the value of _ZO_ECHO.
{__zoxide_cd}
# =============================================================================
#
# Hook configuration for zoxide.
#
# Hook to add new entries to the database.
{__zoxide_hook}
# Initialize hook.
{hook_init}
# =============================================================================
#
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
function __zoxide_z
set argc (count $argv)
if test $argc -eq 0
__zoxide_cd $HOME
else if begin; test $argc -eq 1; and test $argv[1] = '-'; end
__zoxide_cd -
else
set -l __zoxide_result (zoxide query -- $argv)
and __zoxide_cd $__zoxide_result
end
end
# Jump to a directory using interactive search.
function __zoxide_zi
set -l __zoxide_result (zoxide query -i -- $argv)
and __zoxide_cd $__zoxide_result
end
# Add a new entry to the database.
abbr -a __zoxide_za 'zoxide add'
# Query an entry from the database using only keywords.
abbr -a __zoxide_zq 'zoxide query'
# Query an entry from the database using interactive selection.
abbr -a __zoxide_zqi 'zoxide query -i'
# Remove an entry from the database using the exact path.
abbr -a __zoxide_zr 'zoxide remove'
# Remove an entry from the database using interactive selection.
function __zoxide_zri
set -l __zoxide_result (zoxide query -i -- $argv)
and zoxide remove $__zoxide_result
end
# =============================================================================
#
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{aliases}
# =============================================================================
#
# To initialize zoxide with fish, add the following line to your fish
# configuration file (usually ~/.config/fish/config.fish):
#
# zoxide init fish | source
",
__zoxide_pwd = __zoxide_pwd,
__zoxide_cd = __zoxide_cd,
__zoxide_hook = __zoxide_hook,
hook_init = hook_init,
aliases = aliases,
)?;
Ok(())
}

View File

@ -1,10 +1,14 @@
mod shell;
mod bash;
mod fish;
mod posix;
mod powershell;
mod zsh;
use anyhow::Result;
use anyhow::{Context, Result};
use clap::arg_enum;
use structopt::StructOpt;
use std::io::{self, Write};
use std::io;
/// Generates shell configuration
#[derive(Debug, StructOpt)]
@ -17,7 +21,7 @@ pub struct Init {
#[structopt(long, alias = "z-cmd", default_value = "z")]
cmd: String,
/// Prevents zoxide from defining any commands other than 'z'
/// Prevents zoxide from defining any commands
#[structopt(long, alias = "no-define-aliases")]
no_aliases: bool,
@ -33,35 +37,17 @@ pub struct Init {
impl Init {
pub fn run(&self) -> Result<()> {
let config = match self.shell {
Shell::bash => shell::bash::CONFIG,
Shell::fish => shell::fish::CONFIG,
Shell::posix => shell::posix::CONFIG,
Shell::powershell => shell::powershell::CONFIG,
Shell::zsh => shell::zsh::CONFIG,
};
let stdout = io::stdout();
let mut handle = stdout.lock();
let z = config.z;
writeln!(handle, "{}", z(&self.cmd)).unwrap();
if !self.no_aliases {
let alias = config.alias;
writeln!(handle, "{}", alias(&self.cmd)).unwrap();
match self.shell {
Shell::bash => bash::run(&mut handle, self),
Shell::fish => fish::run(&mut handle, self),
Shell::posix => posix::run(&mut handle, self),
Shell::powershell => powershell::run(&mut handle, self),
Shell::zsh => zsh::run(&mut handle, self),
}
match self.hook {
Hook::none => (),
Hook::prompt => writeln!(handle, "{}", config.hook.prompt).unwrap(),
Hook::pwd => {
let hook_pwd = config.hook.pwd;
writeln!(handle, "{}", hook_pwd()?).unwrap();
}
}
Ok(())
.context("could not initialize zoxide")
}
}

View File

@ -0,0 +1,232 @@
use super::{Hook, Init};
use crate::config;
use crate::util;
use std::io::Write;
use anyhow::Result;
use uuid::Uuid;
pub fn run<W: Write>(writer: &mut W, options: &Init) -> Result<()> {
const NOT_CONFIGURED: &str = "\
# -- not configured --";
let __zoxide_pwd = if config::zo_resolve_symlinks() {
"\
__zoxide_pwd() {
pwd -P
}"
} else {
"\
__zoxide_pwd() {
pwd -L
}"
};
let __zoxide_cd = if config::zo_echo() {
"\
__zoxide_cd() {
cd \"$@\" || return \"$?\"
__zoxide_pwd
}"
} else {
"\
__zoxide_cd() {
cd \"$@\" || return \"$?\"
}"
};
let __zoxide_hook = match options.hook {
Hook::none => NOT_CONFIGURED.into(),
Hook::prompt => "\
__zoxide_hook() {
zoxide add \"$(__zoxide_pwd)\"
}"
.into(),
Hook::pwd => {
let mut tmp_path = std::env::temp_dir();
tmp_path.push("zoxide");
let tmp_path_str = util::path_to_str(&tmp_path)?;
let pwd_path = tmp_path.join(format!("pwd-{}", Uuid::new_v4()));
let pwd_path_str = util::path_to_str(&pwd_path)?;
format!(
"\
# PWD hooks in POSIX use a temporary file, located at `$__zoxide_pwd_path`, to track
# changes in the current directory. These files are removed upon restart,
# but they should ideally also be cleaned up once the shell exits using traps.
#
# This can be done as follows:
#
# trap '__zoxide_cleanup' EXIT HUP KILL TERM
# trap '__zoxide_cleanup; trap - INT; kill -s INT \"$$\"' INT
# trap '__zoxide_cleanup; trap - QUIT; kill -s QUIT \"$$\"' QUIT
#
# By default, traps are not set up because they override all previous traps.
# It is therefore up to the user to add traps to their shell configuration.
__zoxide_tmp_path={tmp_path}
__zoxide_pwd_path={pwd_path}
__zoxide_cleanup() {{
rm -f \"$__zoxide_pwd_path\"
}}
__zoxide_setpwd() {{
mkdir -p \"$__zoxide_tmp_path\"
echo \"$PWD\" > \"$__zoxide_pwd_path\"
}}
__zoxide_setpwd
__zoxide_hook() {{
_ZO_OLDPWD=\"$(cat \"$__zoxide_pwd_path\")\"
if [ -z \"$_ZO_OLDPWD\" ] || [ \"$_ZO_OLDPWD\" != \"$PWD\" ]; then
__zoxide_setpwd && zoxide add \"$(pwd -L)\" > /dev/null
fi
}}",
tmp_path = posix_quote(tmp_path_str),
pwd_path = posix_quote(pwd_path_str),
)
}
};
let hook_init = match options.hook {
Hook::none => NOT_CONFIGURED,
_ => {
"\
case \"$PS1\" in
*\\$\\(__zoxide_hook\\)*) ;;
*) PS1=\"${PS1}\\$(__zoxide_hook)\" ;;
esac"
}
};
let aliases = if options.no_aliases {
NOT_CONFIGURED.into()
} else {
format!(
"\
alias {cmd}='__zoxide_z'
alias {cmd}i='__zoxide_zi'
alias {cmd}a='__zoxide_za'
alias {cmd}q='__zoxide_zq'
alias {cmd}qi='__zoxide_zqi'
alias {cmd}r='__zoxide_zr'
alias {cmd}ri='__zoxide_zri'",
cmd = options.cmd
)
};
writeln!(
writer,
"\
# =============================================================================
#
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
{__zoxide_pwd}
# cd + custom logic based on the value of _ZO_ECHO.
{__zoxide_cd}
# =============================================================================
#
# Hook configuration for zoxide.
#
# Hook to add new entries to the database.
{__zoxide_hook}
# Initialize hook.
{hook_init}
# =============================================================================
#
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
__zoxide_z() {{
if [ \"$#\" -eq 0 ]; then
__zoxide_cd ~
elif [ \"$#\" -eq 1 ] && [ \"$1\" = '-' ]; then
if [ -n \"$OLDPWD\" ]; then
__zoxide_cd \"$OLDPWD\"
else
echo \"zoxide: \\$OLDPWD is not set\"
return 1
fi
else
__zoxide_result=\"$(zoxide query -- \"$@\")\" && __zoxide_cd \"$__zoxide_result\"
fi
}}
# Jump to a directory using interactive search.
__zoxide_zi() {{
__zoxide_result=\"$(zoxide query -i -- \"$@\")\" && __zoxide_cd \"$__zoxide_result\"
}}
# Add a new entry to the database.
alias __zoxide_za='zoxide add'
# Query an entry from the database using only keywords.
alias __zoxide_zq='zoxide query'
# Query an entry from the database using interactive selection.
alias __zoxide_zqi='zoxide query -i'
# Remove an entry from the database using the exact path.
alias __zoxide_zr='zoxide remove'
# Remove an entry from the database using interactive selection.
__zoxide_zri() {{
__zoxide_result=\"$(zoxide query -i -- \"$@\")\" && zoxide remove \"$__zoxide_result\"
}}
# =============================================================================
#
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{aliases}
# =============================================================================
#
# To initialize zoxide with your POSIX shell, add the following line to your
# shell configuration file:
#
# eval \"$(zoxide init posix --prompt hook)\"
",
__zoxide_pwd = __zoxide_pwd,
__zoxide_cd = __zoxide_cd,
__zoxide_hook = __zoxide_hook,
hook_init = hook_init,
aliases = aliases,
)?;
Ok(())
}
fn posix_quote(string: &str) -> String {
let mut quoted = String::with_capacity(string.len() + 2);
quoted.push('\'');
for ch in string.chars() {
match ch {
'\\' => quoted.push_str(r"\\"),
'\'' => quoted.push_str(r"'\''"),
_ => quoted.push(ch),
}
}
quoted.push('\'');
quoted
}

View File

@ -0,0 +1,180 @@
use anyhow::Result;
use std::io::Write;
use super::{Hook, Init};
use crate::config;
pub fn run<W: Write>(writer: &mut W, options: &Init) -> Result<()> {
const NOT_CONFIGURED: &str = "\
# -- not configured --";
let __zoxide_pwd = "\
function __zoxide_pwd {
$(Get-Location).Path
}";
let __zoxide_cd = if config::zo_echo() {
"\
function __zoxide_cd($dir) {
Set-Location $dir -ea Stop
__zoxide_pwd
}"
} else {
"\
function __zoxide_cd($dir) {
Set-Location $dir -ea Stop
}"
};
let __zoxide_hook = "\
function __zoxide_hook {
zoxide add $(__zoxide_pwd)
}";
let hook_init = match options.hook {
Hook::none => NOT_CONFIGURED,
Hook::prompt => {
"\
$PreZoxidePrompt = $function:prompt
function prompt {
$null = __zoxide_hook
& $PreZoxidePrompt
}"
}
Hook::pwd => {
"\
if ($PSVersionTable.PSVersion.Major -ge 6) {
$ExecutionContext.InvokeCommand.LocationChangedAction = {
$null = __zoxide_hook
}
} else {
Write-Error \"zoxide: PWD hooks are not supported below PowerShell 6, use 'zoxide init powershell --hook prompt' instead.\"
}"
}
};
let aliases = if options.no_aliases {
NOT_CONFIGURED.into()
} else {
format!(
"\
Set-Alias {cmd} __zoxide_z
Set-Alias {cmd}i __zoxide_zi
Set-Alias {cmd}a __zoxide_za
Set-Alias {cmd}q __zoxide_zq
Set-Alias {cmd}qi __zoxide_zqi
Set-Alias {cmd}r __zoxide_zr
Set-Alias {cmd}ri __zoxide_zri",
cmd = options.cmd
)
};
writeln!(
writer,
"\
# =============================================================================
#
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
{__zoxide_pwd}
# cd + custom logic based on the value of _ZO_ECHO.
{__zoxide_cd}
# =============================================================================
#
# Hook configuration for zoxide.
#
# Hook to add new entries to the database.
{__zoxide_hook}
# Initialize hook.
{hook_init}
# =============================================================================
#
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
function __zoxide_z {{
if ($args.Length -eq 0) {{
__zoxide_cd ~
}}
elseif ($args.Length -eq 1 -and $args[0] -eq '-') {{
__zoxide_cd -
}}
else {{
$__zoxide_result = zoxide query -- @args
if ($LASTEXITCODE -eq 0) {{
__zoxide_cd $__zoxide_result
}}
}}
}}
# Jump to a directory using interactive search.
function zi {{
$__zoxide_result = zoxide query -i -- @args
if ($LASTEXITCODE -eq 0) {{
__zoxide_cd $__zoxide_result
}}
}}
# Add a new entry to the database.
function __zoxide_za {{ zoxide add @args }}
# Query an entry from the database using only keywords.
function __zoxide_zq {{ zoxide query @args }}
# Query an entry from the database using interactive selection.
function __zoxide_zqi {{ zoxide query -i @args }}
# Remove an entry from the database using the exact path.
function __zoxide_zr {{ zoxide remove @args }}
# Remove an entry from the database using interactive selection.
function __zoxide_zri {{
$_zoxide_result = zoxide query -i -- @args
if ($LASTEXITCODE -eq 0) {{
zoxide remove $_zoxide_result
}}
}}
# =============================================================================
#
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{aliases}
# =============================================================================
#
# To initialize zoxide with PowerShell, add the following line to your
# PowerShell configuration file (the location is stored in $profile):
#
# Invoke-Expression (& {{
# $hook = if ($PSVersionTable.PSVersion.Major -ge 6) {{
# 'pwd'
# }} else {{
# 'prompt'
# }}
# (zoxide init powershell --hook $hook) -join \"`n\"
# }})
",
__zoxide_pwd = __zoxide_pwd,
__zoxide_cd = __zoxide_cd,
__zoxide_hook = __zoxide_hook,
hook_init = hook_init,
aliases = aliases,
)?;
Ok(())
}

View File

@ -1,45 +0,0 @@
use super::{posix, HookConfig, ShellConfig};
use anyhow::Result;
use std::borrow::Cow;
pub const CONFIG: ShellConfig = ShellConfig {
z: posix::CONFIG.z,
alias: posix::CONFIG.alias,
hook: HookConfig {
prompt: HOOK_PROMPT,
pwd: hook_pwd,
},
};
const HOOK_PROMPT: &str = r#"
_zoxide_hook() {
zoxide add "$(pwd -L)"
}
case "$PROMPT_COMMAND" in
*_zoxide_hook*) ;;
*) PROMPT_COMMAND="${PROMPT_COMMAND:+${PROMPT_COMMAND};}_zoxide_hook" ;;
esac
"#;
const fn hook_pwd() -> Result<Cow<'static, str>> {
const HOOK_PWD: &str = r#"
_zoxide_hook() {
if [ -z "${_ZO_PWD}" ]; then
_ZO_PWD="${PWD}"
elif [ "${_ZO_PWD}" != "${PWD}" ]; then
_ZO_PWD="${PWD}"
zoxide add "$(pwd -L)"
fi
}
case "$PROMPT_COMMAND" in
*_zoxide_hook*) ;;
*) PROMPT_COMMAND="${PROMPT_COMMAND:+${PROMPT_COMMAND};}_zoxide_hook" ;;
esac
"#;
Ok(Cow::Borrowed(HOOK_PWD))
}

View File

@ -1,84 +0,0 @@
use super::{HookConfig, ShellConfig};
use anyhow::Result;
use std::borrow::Cow;
pub const CONFIG: ShellConfig = ShellConfig {
z,
alias,
hook: HookConfig {
prompt: HOOK_PROMPT,
pwd: hook_pwd,
},
};
fn z(cmd: &str) -> String {
format!(
r#"
function _z_cd
cd $argv
or return $status
commandline -f repaint
if test "$_ZO_ECHO" = "1"
echo $PWD
end
end
function {0}
set argc (count $argv)
if test $argc -eq 0
_z_cd $HOME
else if begin; test $argc -eq 1; and test $argv[1] = '-'; end
_z_cd -
else
set -l _zoxide_result (zoxide query -- $argv)
and _z_cd $_zoxide_result
end
end
function {0}i
set -l _zoxide_result (zoxide query -i -- $argv)
and _z_cd $_zoxide_result
end
"#,
cmd
)
}
fn alias(cmd: &str) -> String {
format!(
r#"
abbr -a {0}a 'zoxide add'
abbr -a {0}q 'zoxide query'
abbr -a {0}qi 'zoxide query -i'
abbr -a {0}r 'zoxide remove'
function {0}ri
set -l _zoxide_result (zoxide query -i -- $argv)
and zoxide remove $_zoxide_result
end
"#,
cmd
)
}
const HOOK_PROMPT: &str = r#"
function _zoxide_hook --on-event fish_prompt
zoxide add (pwd -L)
end
"#;
const fn hook_pwd() -> Result<Cow<'static, str>> {
const HOOK_PWD: &str = r#"
function _zoxide_hook --on-variable PWD
zoxide add (pwd -L)
end
"#;
Ok(Cow::Borrowed(HOOK_PWD))
}

View File

@ -1,20 +0,0 @@
pub mod bash;
pub mod fish;
pub mod posix;
pub mod powershell;
pub mod zsh;
use anyhow::Result;
use std::borrow::Cow;
pub struct ShellConfig {
pub z: fn(&str) -> String,
pub alias: fn(&str) -> String,
pub hook: HookConfig,
}
pub struct HookConfig {
pub prompt: &'static str,
pub pwd: fn() -> Result<Cow<'static, str>>,
}

View File

@ -1,149 +0,0 @@
use super::{HookConfig, ShellConfig};
use crate::util;
use anyhow::Result;
use uuid::Uuid;
use std::borrow::Cow;
pub const CONFIG: ShellConfig = ShellConfig {
z,
alias,
hook: HookConfig {
prompt: HOOK_PROMPT,
pwd: hook_pwd,
},
};
fn z(cmd: &str) -> String {
format!(
r#"
_z_cd() {{
cd "$@" || return "$?"
if [ "$_ZO_ECHO" = "1" ]; then
echo "$PWD"
fi
}}
{0}() {{
if [ "$#" -eq 0 ]; then
_z_cd ~
elif [ "$#" -eq 1 ] && [ "$1" = '-' ]; then
if [ -n "$OLDPWD" ]; then
_z_cd "$OLDPWD"
else
echo 'zoxide: $OLDPWD is not set'
return 1
fi
else
_zoxide_result="$(zoxide query -- "$@")" && _z_cd "$_zoxide_result"
fi
}}
{0}i() {{
_zoxide_result="$(zoxide query -i -- "$@")" && _z_cd "$_zoxide_result"
}}
"#,
cmd
)
}
fn alias(cmd: &str) -> String {
format!(
r#"
alias {0}a='zoxide add'
alias {0}q='zoxide query'
alias {0}qi='zoxide query -i'
alias {0}r='zoxide remove'
{0}ri() {{
_zoxide_result="$(zoxide query -i -- "$@")" && zoxide remove "$_zoxide_result"
}}
"#,
cmd
)
}
const HOOK_PROMPT: &str = r#"
_zoxide_hook() {
zoxide add "$(pwd -L)"
}
case "$PS1" in
*\$\(_zoxide_hook\)*) ;;
*) PS1="${PS1}\$(_zoxide_hook)" ;;
esac
"#;
fn hook_pwd() -> Result<Cow<'static, str>> {
let mut tmp_path = std::env::temp_dir();
tmp_path.push("zoxide");
let tmp_path_str = util::path_to_str(&tmp_path)?;
let pwd_path = tmp_path.join(format!("pwd-{}", Uuid::new_v4()));
let pwd_path_str = util::path_to_str(&pwd_path)?;
let hook_pwd = format!(
r#"
# PWD hooks in POSIX use a temporary file, located at `$_ZO_PWD_PATH`, to track
# changes in the current directory. These files are removed upon restart,
# but they should ideally also be cleaned up once the shell exits using traps.
#
# This can be done as follows:
#
# trap '_zoxide_cleanup' EXIT HUP KILL TERM
# trap '_zoxide_cleanup; trap - INT; kill -s INT "$$"' INT
# trap '_zoxide_cleanup; trap - QUIT; kill -s QUIT "$$"' QUIT
#
# By default, traps are not set up because they override all previous traps.
# It is therefore up to the user to add traps to their shell configuration.
_ZO_TMP_PATH={}
_ZO_PWD_PATH={}
_zoxide_cleanup() {{
rm -f "$_ZO_PWD_PATH"
}}
_zoxide_setpwd() {{
mkdir -p "$_ZO_TMP_PATH"
echo "$PWD" > "$_ZO_PWD_PATH"
}}
_zoxide_setpwd
_zoxide_hook() {{
_ZO_OLDPWD="$(cat "$_ZO_PWD_PATH")"
if [ -z "$_ZO_OLDPWD" ] || [ "$_ZO_OLDPWD" != "$PWD" ]; then
_zoxide_setpwd && zoxide add "$(pwd -L)" > /dev/null
fi
}}
case "$PS1" in
*\$\(_zoxide_hook\)*) ;;
*) PS1="${{PS1}}\$(_zoxide_hook)" ;;
esac"#,
quote(tmp_path_str),
quote(pwd_path_str),
);
Ok(Cow::Owned(hook_pwd))
}
fn quote(string: &str) -> String {
let mut quoted = String::with_capacity(string.len() + 2);
quoted.push('\'');
for ch in string.chars() {
match ch {
'\\' => quoted.push_str(r"\\"),
'\'' => quoted.push_str(r"'\''"),
_ => quoted.push(ch),
}
}
quoted.push('\'');
quoted
}

View File

@ -1,92 +0,0 @@
use super::{HookConfig, ShellConfig};
use anyhow::Result;
use std::borrow::Cow;
pub const CONFIG: ShellConfig = ShellConfig {
z,
alias,
hook: HookConfig {
prompt: HOOK_PROMPT,
pwd: hook_pwd,
},
};
fn z(cmd: &str) -> String {
format!(
r#"
function _z_cd($dir) {{
Set-Location $dir -ea Stop
if ($env:_ZO_ECHO -eq "1") {{
Write-Host "$PWD"
}}
}}
function {0} {{
if ($args.Length -eq 0) {{
_z_cd ~
}}
elseif ($args.Length -eq 1 -and $args[0] -eq '-') {{
_z_cd -
}}
else {{
$_zoxide_result = zoxide query -- @args
if ($LASTEXITCODE -eq 0) {{
_z_cd $_zoxide_result
}}
}}
}}
function {0}i {{
$_zoxide_result = zoxide query -i -- @args
if ($LASTEXITCODE -eq 0) {{
_z_cd $_zoxide_result
}}
}}
"#,
cmd
)
}
fn alias(cmd: &str) -> String {
format!(
r#"
function {0}a {{ zoxide add @args }}
function {0}q {{ zoxide query @args }}
function {0}qi {{ zoxide query -i @args }}
function {0}r {{ zoxide remove @args }}
function {0}ri {{
$_zoxide_result = zoxide query -i -- @args
if ($LASTEXITCODE -eq 0) {{
zoxide remove $_zoxide_result
}}
}}
"#,
cmd
)
}
const HOOK_PROMPT: &str = r#"
$PreZoxidePrompt = $function:prompt
function prompt {
$null = zoxide add $(Get-Location)
& $PreZoxidePrompt
}
"#;
const fn hook_pwd() -> Result<Cow<'static, str>> {
const HOOK_PWD: &str = r#"
if ($PSVersionTable.PSVersion.Major -ge 6) {
$ExecutionContext.InvokeCommand.LocationChangedAction = {
$null = zoxide add $(Get-Location)
}
} else {
Write-Error "pwd hook requires pwsh - use 'zoxide init powershell --hook prompt'"
}
"#;
Ok(Cow::Borrowed(HOOK_PWD))
}

View File

@ -1,36 +0,0 @@
use super::{posix, HookConfig, ShellConfig};
use anyhow::Result;
use std::borrow::Cow;
pub const CONFIG: ShellConfig = ShellConfig {
z: posix::CONFIG.z,
alias: posix::CONFIG.alias,
hook: HookConfig {
prompt: HOOK_PROMPT,
pwd: hook_pwd,
},
};
const HOOK_PROMPT: &str = r#"
_zoxide_hook() {
zoxide add "$(pwd -L)"
}
[[ -n "${precmd_functions[(r)_zoxide_hook]}" ]] || {
precmd_functions+=(_zoxide_hook)
}
"#;
const fn hook_pwd() -> Result<Cow<'static, str>> {
const HOOK_PWD: &str = r#"
_zoxide_hook() {
zoxide add "$(pwd -L)"
}
chpwd_functions=(${chpwd_functions[@]} "_zoxide_hook")
"#;
Ok(Cow::Borrowed(HOOK_PWD))
}

174
src/subcommand/init/zsh.rs Normal file
View File

@ -0,0 +1,174 @@
use anyhow::Result;
use std::io::Write;
use super::{Hook, Init};
use crate::config;
pub fn run<W: Write>(writer: &mut W, options: &Init) -> Result<()> {
const NOT_CONFIGURED: &str = "\
# -- not configured --";
let __zoxide_pwd = if config::zo_resolve_symlinks() {
"\
__zoxide_pwd() {
pwd -P
}"
} else {
"\
__zoxide_pwd() {
pwd -L
}"
};
let __zoxide_cd = if config::zo_echo() {
"\
__zoxide_cd() {
cd \"$@\" || return \"$?\"
__zoxide_pwd
}"
} else {
"\
__zoxide_cd() {
cd \"$@\" || return \"$?\"
}"
};
let __zoxide_hook = match options.hook {
Hook::none => NOT_CONFIGURED,
_ => {
"\
__zoxide_hook() {
zoxide add \"$(__zoxide_pwd)\"
}"
}
};
let hook_init = match options.hook {
Hook::none => NOT_CONFIGURED,
Hook::prompt => {
"\
[[ -n \"${precmd_functions[(r)__zoxide_hook]}\" ]] || {
precmd_functions+=(__zoxide_hook)
}"
}
Hook::pwd => {
"\
chpwd_functions=(${chpwd_functions[@]} \"__zoxide_hook\")"
}
};
let aliases = if options.no_aliases {
NOT_CONFIGURED.into()
} else {
format!(
"\
alias {cmd}='__zoxide_z'
alias {cmd}i='__zoxide_zi'
alias {cmd}a='__zoxide_za'
alias {cmd}q='__zoxide_zq'
alias {cmd}qi='__zoxide_zqi'
alias {cmd}r='__zoxide_zr'
alias {cmd}ri='__zoxide_zri'",
cmd = options.cmd
)
};
write!(
writer,
"\
# =============================================================================
#
# Utility functions for zoxide.
#
# pwd based on the value of _ZO_RESOLVE_SYMLINKS.
{__zoxide_pwd}
# cd + custom logic based on the value of _ZO_ECHO.
{__zoxide_cd}
# =============================================================================
#
# Hook configuration for zoxide.
#
# Hook to add new entries to the database.
{__zoxide_hook}
# Initialize hook.
{hook_init}
# =============================================================================
#
# When using zoxide with --no-aliases, alias these internal functions as
# desired.
#
# Jump to a directory using only keywords.
__zoxide_z() {{
if [ \"$#\" -eq 0 ]; then
__zoxide_cd ~
elif [ \"$#\" -eq 1 ] && [ \"$1\" = '-' ]; then
if [ -n \"$OLDPWD\" ]; then
__zoxide_cd \"$OLDPWD\"
else
echo \"zoxide: \\$OLDPWD is not set\"
return 1
fi
else
local __zoxide_result
__zoxide_result=\"$(zoxide query -- \"$@\")\" && __zoxide_cd \"$__zoxide_result\"
fi
}}
# Jump to a directory using interactive search.
__zoxide_zi() {{
local __zoxide_result
__zoxide_result=\"$(zoxide query -i -- \"$@\")\" && __zoxide_cd \"$__zoxide_result\"
}}
# Add a new entry to the database.
alias __zoxide_za='zoxide add'
# Query an entry from the database using only keywords.
alias __zoxide_zq='zoxide query'
# Query an entry from the database using interactive selection.
alias __zoxide_zqi='zoxide query -i'
# Remove an entry from the database using the exact path.
alias __zoxide_zr='zoxide remove'
# Remove an entry from the database using interactive selection.
__zoxide_zri() {{
local __zoxide_result
__zoxide_result=\"$(zoxide query -i -- \"$@\")\" && zoxide remove \"$__zoxide_result\"
}}
# =============================================================================
#
# Convenient aliases for zoxide. Disable these using --no-aliases.
#
{aliases}
# =============================================================================
#
# To initialize zoxide with zsh, add the following line to your zsh
# configuration file (usually ~/.zshrc):
#
# eval \"$(zoxide init zsh)\"
",
__zoxide_pwd = __zoxide_pwd,
__zoxide_cd = __zoxide_cd,
__zoxide_hook = __zoxide_hook,
hook_init = hook_init,
aliases = aliases,
)?;
Ok(())
}