Add plugin: fz

This commit is contained in:
Henry Chang 2017-03-19 15:13:19 -07:00 committed by Henry Chang
parent b908feebcf
commit 337bfd5f72
No known key found for this signature in database
GPG Key ID: 9890185D083B1371
7 changed files with 625 additions and 0 deletions

21
plugins/fz/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Henry Chang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

144
plugins/fz/README-zh.md Normal file
View File

@ -0,0 +1,144 @@
# fz
完全不須再綁定其他熱鍵fz 無縫接軌地為 [z](https://github.com/rupa/z) 的 tab 自動完成自動補全、自動補完、tab completion加上模糊搜尋功能。使用者可以透過此程式在歷史目錄中自由跳躍。支援 Bash 與 zsh。
* [展示](#展示)
* [安裝程序](#安裝程序)
* [macOS](#macos)
* [Bash](#bash)
* [zsh](#zsh)
* [Ubuntu](#ubuntu)
* [Bash](#bash-1)
* [zsh](#zsh-1)
* [使用說明](#使用說明)
* [相關資訊](#相關資訊)
* [授權條款](#授權條款)
## 展示
![示意圖](fz-demo.gif)
## 安裝程序
由 shell 中 source 對應的程式檔案即可使用。但因本程式仰仗 [z](https://github.com/rupa/z) 與 [fzf](https://github.com/junegunn/fzf),故此二者亦須一併安裝至系統中。
注意:`fz` 需在 `z` 之後 source。
### macOS
#### Bash
1. 經由 [Homebrew](https://brew.sh/) 安裝 fzf
```sh
brew install fzf
```
2. 下載 z 與 fz
```sh
mkdir ~/.bash_completion.d
curl "https://raw.githubusercontent.com/rupa/z/master/{z.sh}" \
-o ~/.bash_completion.d/"#1"
curl "https://raw.githubusercontent.com/changyuheng/fz/master/{fz.sh}" \
-o ~/.bash_completion.d/z"#1"
```
3. 在 `~/.bashrc` 中加入以下資訊:
```sh
if [ -d ~/.bash_completion.d ]; then
for file in ~/.bash_completion.d/*; do
. $file
done
fi
```
#### zsh
1. 經由 [Homebrew](https://brew.sh/) 安裝 fzf
```sh
brew install fzf
```
2. 透過 [zplug](https://github.com/zplug/zplug) 部署 z 與 fz。將以下資訊加入 `~/.zshrc`
```sh
zplug "changyuheng/fz", defer:1
zplug "rupa/z", use:z.sh
```
### Ubuntu
#### Bash
1. 安裝 fzf
```sh
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
```
2. 下載 z 與 fz
```sh
mkdir ~/.bash_completion.d
curl "https://raw.githubusercontent.com/rupa/z/master/{z.sh}" \
-o ~/.bash_completion.d/"#1"
curl "https://raw.githubusercontent.com/changyuheng/fz/master/{fz.sh}" \
-o ~/.bash_completion.d/z"#1"
```
3. 在 `~/.bashrc` 中加入以下資訊:
```sh
if [ -d ~/.bash_completion.d ]; then
for file in ~/.bash_completion.d/*; do
. $file
done
fi
```
#### zsh
1. 安裝 fzf
```sh
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
```
2. 透過 [zplug](https://github.com/zplug/zplug) 部署 z 與 fz。將以下資訊加入 `~/.zshrc`
```sh
zplug "changyuheng/fz", defer:1
zplug "rupa/z", use:z.sh
```
## 使用說明
```
z [dir name slug]<TAB>
zz [dir name slug]<TAB>
```
- 程式的功能與 [z](https://github.com/rupa/z) 雷同。`zz` 指令限制搜尋範圍為當前目錄及其子目錄。
- `tab`/`shift-tab`、`ctrl-n`/`ctrl-p`、`ctrl-j`/`ctrl-k` 選擇下一個、上一個選項。`Enter` 確定。
- `FZ_CMD=z` 指定指令名稱。預設為 `z`
- `FZ_SUBDIR_CMD=zz` 指定指令名稱。預設為 `zz`
- `FZ_SUBDIR_TRAVERSAL=0` 關閉子目錄補完。預設為開啟。
- `FZ_CASE_INSENSITIVE=0` 關閉子目錄補完不限大小寫。預設為開啟。
- `FZ_ABBREVIATE_HOME=0` 不展開 `~` 變數。預設為展開。
## 相關資訊
- [cdr](https://github.com/willghatch/zsh-cdr) + [zaw](https://github.com/zsh-users/zaw)
- fzf 的[自動完成說明](https://github.com/junegunn/fzf#fuzzy-completion-for-bash-and-zsh)及其[維基頁面](https://github.com/junegunn/fzf/wiki)
- [fasd](https://github.com/clvv/fasd)
- [autojump](https://github.com/wting/autojump)
- [命令行上的narrowing随着输入逐步减少备选项工具](http://www.cnblogs.com/bamanzi/p/cli-narrowing-tools.html)
## 授權條款
本軟體以 [MIT 授權條款](LICENSE)授權。

161
plugins/fz/README.md Normal file
View File

@ -0,0 +1,161 @@
# fz
A shell plugin that seamlessly adds fuzzy search to tab completion of
[z](https://github.com/rupa/z),
lets you easy to jump around among your historical directories.
Not any additional key binding is needed. Currently supports Bash and zsh.
* [Demo](#demo)
* [Installation](#installation)
* [macOS](#macos)
* [Bash](#bash)
* [zsh](#zsh)
* [Ubuntu](#ubuntu)
* [Bash](#bash-1)
* [zsh](#zsh-1)
* [Usage](#usage)
* [See Also](#see-also)
* [License](#license)
## Demo
![gif-demo](fz-demo.gif)
## Installation
By simply sourcing corresponding script file for your shell, you're all set.
However, this plugin is sitting on top of [z](https://github.com/rupa/z) and
[fzf](https://github.com/junegunn/fzf), so you must have them installed as well.
N.B. `fz` needs to be sourced after `z`.
### macOS
#### Bash
1. Install fzf via [Homebrew](https://brew.sh/).
```sh
brew install fzf
```
2. Download z and fz.
```sh
mkdir ~/.bash_completion.d
curl "https://raw.githubusercontent.com/rupa/z/master/{z.sh}" \
-o ~/.bash_completion.d/"#1"
curl "https://raw.githubusercontent.com/changyuheng/fz/master/{fz.sh}" \
-o ~/.bash_completion.d/z"#1"
```
3. Add the following content to `~/.bashrc`:
```sh
if [ -d ~/.bash_completion.d ]; then
for file in ~/.bash_completion.d/*; do
. $file
done
fi
```
#### zsh
1. Install fzf via [Homebrew](https://brew.sh/).
```sh
brew install fzf
```
2. Install z and fz via [zplug](https://github.com/zplug/zplug).
Add the following content to `~/.zshrc`:
```sh
zplug "changyuheng/fz", defer:1
zplug "rupa/z", use:z.sh
```
### Ubuntu
#### Bash
1. Install fzf.
```sh
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
```
2. Download z and fz.
```sh
mkdir ~/.bash_completion.d
curl "https://raw.githubusercontent.com/rupa/z/master/{z.sh}" \
-o ~/.bash_completion.d/"#1"
curl "https://raw.githubusercontent.com/changyuheng/fz/master/{fz.sh}" \
-o ~/.bash_completion.d/z"#1"
```
3. Add the following content to `~/.bashrc`:
```sh
if [ -d ~/.bash_completion.d ]; then
for file in ~/.bash_completion.d/*; do
. $file
done
fi
```
#### zsh
1. Install fzf.
```sh
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
```
2. Install z and fz via [zplug](https://github.com/zplug/zplug).
Add the following content to `~/.zshrc`:
```sh
zplug "changyuheng/fz", defer:1
zplug "rupa/z", use:z.sh
```
## Usage
```
z [dir name slug]<TAB>
zz [dir name slug]<TAB>
```
- The function of fz is pretty much like what it is of
[z](https://github.com/rupa/z).
`zz` limits the search base starting from current working directory.
Check zs doc for more information.
- `tab`/`shift-tab`, `ctrl-n`/`ctrl-p`, `ctrl-j`/`ctrl-k`,
for next and previous item. `Enter` for selection.
Check fzfs [doc](https://github.com/junegunn/fzf#search-syntax)
for the search syntaxes.
- `FZ_CMD=z` specifies command name of `fz`. Default is `z`.
- `FZ_SUBDIR_CMD=zz` specifies command name for subdirectory only `z`.
Default is `zz`.
- `FZ_SUBDIR_TRAVERSAL=0` disables subdirectory completion.
Default is enabled.
- `FZ_CASE_INSENSITIVE=0` disables case-insensitive subdirectory completion.
Default is enabled.
- `FZ_ABBREVIATE_HOME=0` disables abbreviating `~`. Default is enabled.
## See Also
- [cdr](https://github.com/willghatch/zsh-cdr) + [zaw](https://github.com/zsh-users/zaw)
- fzfs [readme of completion](https://github.com/junegunn/fzf#fuzzy-completion-for-bash-and-zsh)
and its [wiki](https://github.com/junegunn/fzf/wiki)
- [fasd](https://github.com/clvv/fasd)
- [autojump](https://github.com/wting/autojump)
- [命令行上的narrowing随着输入逐步减少备选项工具](http://www.cnblogs.com/bamanzi/p/cli-narrowing-tools.html)
## License
This software is licensed under a [MIT License](LICENSE).

BIN
plugins/fz/fz-demo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

1
plugins/fz/fz.bash Symbolic link
View File

@ -0,0 +1 @@
fz.sh

1
plugins/fz/fz.plugin.zsh Symbolic link
View File

@ -0,0 +1 @@
fz.sh

297
plugins/fz/fz.sh Normal file
View File

@ -0,0 +1,297 @@
[[ -n "$FZ_CMD" ]] || FZ_CMD=z
[[ -n "$FZ_SUBDIR_CMD" ]] || FZ_SUBDIR_CMD=zz
[[ -n "$FZ_HISTORY_CD_CMD" ]] || FZ_HISTORY_CD_CMD=_z
[[ -n "$FZ_SUBDIR_HISTORY_CD_CMD" ]] || FZ_SUBDIR_HISTORY_CD_CMD="_z -c"
[[ -n "$FZ_HISTORY_LIST_GENERATOR" ]] \
|| FZ_HISTORY_LIST_GENERATOR=__fz_generate_matched_history_list
[[ -n "$FZ_SUBDIR_HISTORY_LIST_GENERATOR" ]] \
|| FZ_SUBDIR_HISTORY_LIST_GENERATOR=__fz_generate_matched_subdir_history_list
[[ -n "$FZ_SUBDIR_TRAVERSAL" ]] || FZ_SUBDIR_TRAVERSAL=1
[[ -n "$FZ_CASE_INSENSITIVE" ]] || FZ_CASE_INSENSITIVE=1
[[ -n "$FZ_ABBREVIATE_HOME" ]] || FZ_ABBREVIATE_HOME=1
alias ${FZ_CMD}='_fz'
alias ${FZ_SUBDIR_CMD}='_fzz'
__fz_generate_matched_subdir_list() {
local dir seg starts_with_dir
if [[ "$1" == */ ]]; then
dir="$1"
find -L "$(cd "$dir" 2>/dev/null && pwd)" -mindepth 1 -maxdepth 1 -type d \
2>/dev/null | while read -r line; do
base="${line##*/}"
if [[ "$base" == .* ]]; then
continue
fi
echo "$line"
done
else
dir=$(dirname -- "$1")
seg=$(basename -- "$1")
if [[ "$FZ_CASE_INSENSITIVE" == "1" ]]; then
seg=$(echo "$seg" | tr '[:upper:]' '[:lower:]')
fi
starts_with_dir=$( \
find -L "$(cd "$dir" 2>/dev/null && pwd)" -mindepth 1 -maxdepth 1 \
-type d 2>/dev/null | while read -r line; do \
base="${line##*/}"
if [[ "$seg" != .* && "$base" == .* ]]; then
continue
fi
if [[ "$FZ_CASE_INSENSITIVE" != "1" ]]; then
if [[ "$base" == "$seg"* ]]; then
echo "$line"
fi
else
if [[ -n "$BASH_VERSION" ]]; then
if [[ "${base,,}" == "${seg,,}"* ]]; then
echo "$line"
fi
elif [[ -n "$ZSH_VERSION" ]]; then
if [[ "${base:l}" == "${seg:l}"* ]]; then
echo "$line"
fi
fi
fi
done
)
if [ -n "$starts_with_dir" ]; then
echo "$starts_with_dir"
else
find -L "$(cd "$dir" 2>/dev/null && pwd)" -mindepth 1 -maxdepth 1 \
-type d 2>/dev/null | while read -r line; do \
base="${line##*/}"
if [[ "$seg" != .* && "$base" == .* ]]; then
continue
fi
if [[ "$FZ_CASE_INSENSITIVE" != "1" ]]; then
if [[ "$base" == *"$seg"* ]]; then
echo "$line"
fi
else
if [[ -n "$BASH_VERSION" ]]; then
if [[ "${base,,}" == *"${seg,,}"* ]]; then
echo "$line"
fi
elif [[ -n "$ZSH_VERSION" ]]; then
if [[ "${base:l}" == *"${seg:l}"* ]]; then
echo "$line"
fi
fi
fi
done
fi
fi
}
__fz_generate_matched_history_list() {
_z -l $@ 2>&1 | while read -r line; do
if [[ "$line" == common:* ]]; then continue; fi
# Reverse the order and cut off the scores
echo "$line"
done | sed '1!G;h;$!d' | cut -b 12-
}
__fz_generate_matched_subdir_history_list() {
__fz_generate_matched_history_list -c "$@"
}
__fz_generate_matches() {
local cmd histories subdirs
if [[ -n "$BASH_VERSION" ]]; then
cmd=$([[ "${COMP_WORDS[0]}" =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]; \
echo -n "${BASH_REMATCH[1]}")
elif [[ -n "$ZSH_VERSION" ]]; then
cmd=(${(z)LBUFFER})
cmd=${cmd[1]}
fi
if [[ "$cmd" == "$FZ_CMD" ]]; then
if [[ "$FZ_ABBREVIATE_HOME" == "1" ]]; then
if [ "$FZ_SUBDIR_TRAVERSAL" == "1" ]; then
cat <("$FZ_HISTORY_LIST_GENERATOR" "$@") \
<(__fz_generate_matched_subdir_list "$@") \
| sed '/^$/d' | sed -e "s,^$HOME,~," | awk '!seen[$0]++'
else
cat <("$FZ_HISTORY_LIST_GENERATOR" "$@") \
| sed '/^$/d' | sed -e "s,^$HOME,~," | awk '!seen[$0]++'
fi
else
if [ "$FZ_SUBDIR_TRAVERSAL" == "1" ]; then
cat <("$FZ_HISTORY_LIST_GENERATOR" "$@") \
<(__fz_generate_matched_subdir_list "$@") \
| sed '/^$/d' | awk '!seen[$0]++'
else
cat <("$FZ_HISTORY_LIST_GENERATOR" "$@") \
| sed '/^$/d' | awk '!seen[$0]++'
fi
fi
elif [[ "$cmd" == "$FZ_SUBDIR_CMD" ]]; then
histories=$("$FZ_SUBDIR_HISTORY_LIST_GENERATOR" "$@")
if [[ "$FZ_ABBREVIATE_HOME" == "1" ]]; then
if [ "$FZ_SUBDIR_TRAVERSAL" == "1" ]; then
cat <("$FZ_SUBDIR_HISTORY_LIST_GENERATOR" "$@") \
<(__fz_generate_matched_subdir_list "$@") \
| sed '/^$/d' | sed -e "s,^$HOME,~," | awk '!seen[$0]++'
else
cat <("$FZ_SUBDIR_HISTORY_LIST_GENERATOR" "$@") \
| sed '/^$/d' | sed -e "s,^$HOME,~," | awk '!seen[$0]++'
fi
else
if [ "$FZ_SUBDIR_TRAVERSAL" == "1" ]; then
cat <("$FZ_SUBDIR_HISTORY_LIST_GENERATOR" "$@") \
<(__fz_generate_matched_subdir_list "$@") \
| sed '/^$/d' | awk '!seen[$0]++'
else
cat <("$FZ_SUBDIR_HISTORY_LIST_GENERATOR" "$@") \
| sed '/^$/d' | awk '!seen[$0]++'
fi
fi
fi
}
__fz_bash_completion() {
COMPREPLY=()
local selected slug
eval "slug=${COMP_WORDS[@]:(-1)}"
if [[ "$(__fz_generate_matches "$slug" | head | wc -l)" -gt 1 ]]; then
selected=$(__fz_generate_matches "$slug" \
| FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse \
--bind 'shift-tab:up,tab:down' $FZF_DEFAULT_OPTS" fzf)
elif [[ "$(__fz_generate_matches "$slug" | head | wc -l)" -eq 1 ]]; then
selected=$(__fz_generate_matches "$slug")
else
return
fi
if [[ -n "$selected" ]]; then
if [[ "$FZ_ABBREVIATE_HOME" == "1" ]]; then
selected=${selected/#\~/$HOME}
fi
selected=$(printf %q "$selected")
if [[ "$selected" != */ ]]; then
selected="${selected}/"
fi
if [[ "$FZ_ABBREVIATE_HOME" == "1" ]]; then
selected=${selected/#$HOME/\~}
fi
COMPREPLY=( "$selected" )
fi
printf '\e[5n'
}
__fz_zsh_completion() {
setopt localoptions noshwordsplit noksh_arrays noposixbuiltins nonomatch
local args cmd selected slug
args=(${(z)LBUFFER})
cmd=${args[1]}
if [[ "$cmd" != "$FZ_CMD" && "$cmd" != "$FZ_SUBDIR_CMD" ]] \
|| [[ "$cmd" == "$FZ_CMD" && "$LBUFFER" =~ "^\s*$FZ_CMD$" ]] \
|| [[ "$cmd" == "$FZ_SUBDIR_CMD" && "$LBUFFER" =~ "^\s*$FZ_SUBDIR_CMD$" ]]; then
zle ${__fz_zsh_default_completion:-expand-or-complete}
return
fi
if [[ "${#args}" -gt 1 ]]; then
eval "slug=${args[-1]}"
fi
if [[ "$(__fz_generate_matches "$slug" | head | wc -l)" -gt 1 ]]; then
selected=$(__fz_generate_matches "$slug" \
| FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse \
--bind 'shift-tab:up,tab:down' $FZF_DEFAULT_OPTS" fzf)
elif [[ "$(__fz_generate_matches "$slug" | head | wc -l)" -eq 1 ]]; then
selected=$(__fz_generate_matches "$slug")
else
return
fi
if [[ -n "$selected" ]]; then
if [[ "$FZ_ABBREVIATE_HOME" == "1" ]]; then
selected=${selected/#\~/$HOME}
fi
selected="${(q)selected}"
if [[ "$selected" != */ ]]; then
selected="${selected}/"
fi
if [[ "$FZ_ABBREVIATE_HOME" == "1" ]]; then
selected=${selected/#$HOME/\~}
fi
LBUFFER="$cmd $selected"
fi
zle redisplay
typeset -f zle-line-init >/dev/null && zle zle-line-init
}
__fz_init_bash_completion() {
# Enable redrawing line by printf '\e[5n'
bind '"\e[0n": redraw-current-line'
complete -o nospace -F __fz_bash_completion "$FZ_CMD"
complete -o nospace -F __fz_bash_completion "$FZ_SUBDIR_CMD"
}
__fz_init_zsh_completion() {
[ -n "$__fz_zsh_default_completion" ] || {
binding=$(bindkey '^I')
# $binding[(s: :w)2]
# The command substitution and following word splitting to determine the
# default zle widget for ^I formerly only works if the IFS parameter contains
# a space via $binding[(w)2]. Now it specifically splits at spaces, regardless
# of IFS.
# Its not compatitable with bash so use awk instead.
[[ $binding =~ 'undefined-key' ]] \
|| __fz_zsh_default_completion=$(echo "$binding" | awk '{print $2}')
unset binding
}
zle -N __fz_zsh_completion
bindkey '^I' __fz_zsh_completion
}
_fz() {
local rc
if [[ "$($FZ_HISTORY_LIST_GENERATOR "$@" | head | wc -l)" -gt 0 ]]; then
"$FZ_HISTORY_CD_CMD" "$@"
elif [[ "$FZ_SUBDIR_TRAVERSAL" -ne 0 ]]; then
err=$(cd "${@: -1}" 2>&1)
rc=$?
if ! cd "${@: -1}" 2>/dev/null; then
echo ${err#* } >&2
return $rc
fi
fi
}
_fzz() {
local rc
if [[ "$($FZ_SUBDIR_HISTORY_LIST_GENERATOR "$@" | head | wc -l)" -gt 0 ]]; then
if [[ -n "$BASH_VERSION" ]]; then
$FZ_SUBDIR_HISTORY_CD_CMD "$@"
elif [[ -n "$ZSH_VERSION" ]]; then
${=FZ_SUBDIR_HISTORY_CD_CMD} "$@"
fi
elif [[ "$FZ_SUBDIR_TRAVERSAL" -ne 0 ]]; then
err=$(cd "${@: -1}" 2>&1)
rc=$?
if ! cd "${@: -1}" 2>/dev/null; then
echo ${err#* } >&2
return $rc
fi
fi
}
if [[ -n "$BASH_VERSION" ]]; then
__fz_init_bash_completion
elif [[ -n "$ZSH_VERSION" ]]; then
__fz_init_zsh_completion
fi