summaryrefslogtreecommitdiffstats
path: root/shell
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2015-03-13 13:08:42 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2015-03-13 17:41:00 +0900
commit3935aa84d8cc66c0371926c61afe89832f00df10 (patch)
tree1c5ae8f1cdf7f6cf850ae296e12b10c1bef9de2e /shell
parentdd6138a6559363871d88c594ab9d170e36959129 (diff)
Refactor shell extensions
- Use symlinks instead of generating the full content - Update fish_user_paths and remove ~/.config/fish/functions/fzf.fish - Create wrapper script for fzf when Ruby version and use it instead of exported function not to break fzf-tmux
Diffstat (limited to 'shell')
-rw-r--r--shell/completion.bash242
-rw-r--r--shell/key-bindings.bash75
-rw-r--r--shell/key-bindings.fish79
-rw-r--r--shell/key-bindings.zsh59
4 files changed, 455 insertions, 0 deletions
diff --git a/shell/completion.bash b/shell/completion.bash
new file mode 100644
index 00000000..e609d2be
--- /dev/null
+++ b/shell/completion.bash
@@ -0,0 +1,242 @@
+#!/bin/bash
+# ____ ____
+# / __/___ / __/
+# / /_/_ / / /_
+# / __/ / /_/ __/
+# /_/ /___/_/-completion.bash
+#
+# - $FZF_COMPLETION_TRIGGER (default: '**')
+# - $FZF_COMPLETION_OPTS (default: empty)
+
+_fzf_orig_completion_filter() {
+ sed 's/.*-F *\([^ ]*\).* \([^ ]*\)$/export _fzf_orig_completion_\2=\1;/' |
+ sed 's/[^a-z0-9_= ;]/_/g'
+}
+
+_fzf_opts_completion() {
+ local cur opts
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ opts="
+ -x --extended
+ -e --extended-exact
+ -i +i
+ -n --nth
+ -d --delimiter
+ +s --no-sort
+ --tac
+ -m --multi
+ --no-mouse
+ +c --no-color
+ +2 --no-256
+ --black
+ --reverse
+ --prompt
+ -q --query
+ -1 --select-1
+ -0 --exit-0
+ -f --filter
+ --print-query
+ --sync"
+
+ if [[ ${cur} =~ ^-|\+ ]]; then
+ COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+ return 0
+ fi
+
+ return 0
+}
+
+_fzf_handle_dynamic_completion() {
+ local cmd orig ret
+ cmd="$1"
+ shift
+
+ orig=$(eval "echo \$_fzf_orig_completion_$cmd")
+ if [ -n "$orig" ] && type "$orig" > /dev/null 2>&1; then
+ $orig "$@"
+ elif [ -n "$_fzf_completion_loader" ]; then
+ _completion_loader "$@"
+ ret=$?
+ eval $(complete | \grep "\-F.* $cmd$" | _fzf_orig_completion_filter)
+ source $BASH_SOURCE
+ return $ret
+ fi
+}
+
+_fzf_path_completion() {
+ local cur base dir leftover matches trigger cmd
+ cmd=$(echo ${COMP_WORDS[0]} | sed 's/[^a-z0-9_=]/_/g')
+ COMPREPLY=()
+ trigger=${FZF_COMPLETION_TRIGGER:-**}
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ if [[ ${cur} == *"$trigger" ]]; then
+ base=${cur:0:${#cur}-${#trigger}}
+ eval base=$base
+
+ dir="$base"
+ while [ 1 ]; do
+ if [ -z "$dir" -o -d "$dir" ]; then
+ leftover=${base/#"$dir"}
+ leftover=${leftover/#\/}
+ [ "$dir" = './' ] && dir=''
+ tput sc
+ matches=$(find -L "$dir"* $1 2> /dev/null | fzf $FZF_COMPLETION_OPTS $2 -q "$leftover" | while read item; do
+ printf "%q$3 " "$item"
+ done)
+ matches=${matches% }
+ if [ -n "$matches" ]; then
+ COMPREPLY=( "$matches" )
+ else
+ COMPREPLY=( "$cur" )
+ fi
+ tput rc
+ return 0
+ fi
+ dir=$(dirname "$dir")
+ [[ "$dir" =~ /$ ]] || dir="$dir"/
+ done
+ else
+ shift
+ shift
+ shift
+ _fzf_handle_dynamic_completion "$cmd" "$@"
+ fi
+}
+
+_fzf_list_completion() {
+ local cur selected trigger cmd src
+ read -r src
+ cmd=$(echo ${COMP_WORDS[0]} | sed 's/[^a-z0-9_=]/_/g')
+ trigger=${FZF_COMPLETION_TRIGGER:-**}
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ if [[ ${cur} == *"$trigger" ]]; then
+ cur=${cur:0:${#cur}-${#trigger}}
+
+ tput sc
+ selected=$(eval "$src | fzf $FZF_COMPLETION_OPTS $1 -q '$cur'" | tr '\n' ' ')
+ selected=${selected% }
+ tput rc
+
+ if [ -n "$selected" ]; then
+ COMPREPLY=("$selected")
+ return 0
+ fi
+ else
+ shift
+ _fzf_handle_dynamic_completion "$cmd" "$@"
+ fi
+}
+
+_fzf_all_completion() {
+ _fzf_path_completion \
+ "-name .git -prune -o -name .svn -prune -o -type d -print -o -type f -print -o -type l -print" \
+ "-m" "" "$@"
+}
+
+_fzf_file_completion() {
+ _fzf_path_completion \
+ "-name .git -prune -o -name .svn -prune -o -type f -print -o -type l -print" \
+ "-m" "" "$@"
+}
+
+_fzf_dir_completion() {
+ _fzf_path_completion \
+ "-name .git -prune -o -name .svn -prune -o -type d -print" \
+ "" "/" "$@"
+}
+
+_fzf_kill_completion() {
+ [ -n "${COMP_WORDS[COMP_CWORD]}" ] && return 1
+
+ local selected
+ tput sc
+ selected=$(ps -ef | sed 1d | fzf -m $FZF_COMPLETION_OPTS | awk '{print $2}' | tr '\n' ' ')
+ tput rc
+
+ if [ -n "$selected" ]; then
+ COMPREPLY=( "$selected" )
+ return 0
+ fi
+}
+
+_fzf_telnet_completion() {
+ _fzf_list_completion '+m' "$@" << "EOF"
+ \grep -v '^\s*\(#\|$\)' /etc/hosts | awk '{if (length($2) > 0) {print $2}}' | sort -u
+EOF
+}
+
+_fzf_ssh_completion() {
+ _fzf_list_completion '+m' "$@" << "EOF"
+ cat <(cat ~/.ssh/config /etc/ssh/ssh_config 2> /dev/null | \grep -i ^host | \grep -v '*') <(\grep -v '^\s*\(#\|$\)' /etc/hosts) | awk '{print $2}' | sort -u
+EOF
+}
+
+_fzf_env_var_completion() {
+ _fzf_list_completion '-m' "$@" << "EOF"
+ declare -xp | sed 's/=.*//' | sed 's/.* //'
+EOF
+}
+
+_fzf_alias_completion() {
+ _fzf_list_completion '-m' "$@" << "EOF"
+ alias | sed 's/=.*//' | sed 's/.* //'
+EOF
+}
+
+# fzf options
+complete -F _fzf_opts_completion fzf
+
+d_cmds="cd pushd rmdir"
+f_cmds="
+ awk cat diff diff3
+ emacs ex file ftp g++ gcc gvim head hg java
+ javac ld less more mvim patch perl python ruby
+ sed sftp sort source tail tee uniq vi view vim wc"
+a_cmds="
+ basename bunzip2 bzip2 chmod chown curl cp dirname du
+ find git grep gunzip gzip hg jar
+ ln ls mv open rm rsync scp
+ svn tar unzip zip"
+x_cmds="kill ssh telnet unset unalias export"
+
+# Preserve existing completion
+if [ "$_fzf_completion_loaded" != '0.8.6-1' ]; then
+ # Really wish I could use associative array but OSX comes with bash 3.2 :(
+ eval $(complete | \grep '\-F' | \grep -v _fzf_ |
+ \grep -E " ($(echo $d_cmds $f_cmds $a_cmds $x_cmds | sed 's/ /|/g' | sed 's/+/\\+/g'))$" | _fzf_orig_completion_filter)
+ export _fzf_completion_loaded=0.8.6-1
+fi
+
+if type _completion_loader > /dev/null 2>&1; then
+ _fzf_completion_loader=1
+fi
+
+# Directory
+for cmd in $d_cmds; do
+ complete -F _fzf_dir_completion -o nospace -o plusdirs $cmd
+done
+
+# File
+for cmd in $f_cmds; do
+ complete -F _fzf_file_completion -o default -o bashdefault $cmd
+done
+
+# Anything
+for cmd in $a_cmds; do
+ complete -F _fzf_all_completion -o default -o bashdefault $cmd
+done
+
+# Kill completion
+complete -F _fzf_kill_completion -o nospace -o default -o bashdefault kill
+
+# Host completion
+complete -F _fzf_ssh_completion -o default -o bashdefault ssh
+complete -F _fzf_telnet_completion -o default -o bashdefault telnet
+
+# Environment variables / Aliases
+complete -F _fzf_env_var_completion -o default -o bashdefault unset
+complete -F _fzf_env_var_completion -o default -o bashdefault export
+complete -F _fzf_alias_completion -o default -o bashdefault unalias
+
+unset cmd d_cmds f_cmds a_cmds x_cmds
diff --git a/shell/key-bindings.bash b/shell/key-bindings.bash
new file mode 100644
index 00000000..b7c79463
--- /dev/null
+++ b/shell/key-bindings.bash
@@ -0,0 +1,75 @@
+# Key bindings
+# ------------
+__fsel() {
+ command find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
+ -o -type f -print \
+ -o -type d -print \
+ -o -type l -print 2> /dev/null | sed 1d | cut -b3- | fzf -m | while read item; do
+ printf '%q ' "$item"
+ done
+ echo
+}
+
+if [[ $- =~ i ]]; then
+
+__fsel_tmux() {
+ local height
+ height=${FZF_TMUX_HEIGHT:-40%}
+ if [[ $height =~ %$ ]]; then
+ height="-p ${height%\%}"
+ else
+ height="-l $height"
+ fi
+ tmux split-window $height "bash -c 'source ~/.fzf.bash; tmux send-keys -t $TMUX_PANE \"\$(__fsel)\"'"
+}
+
+__fcd() {
+ local dir
+ dir=$(command find -L ${1:-.} \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
+ -o -type d -print 2> /dev/null | sed 1d | cut -b3- | fzf +m) && printf 'cd %q' "$dir"
+}
+
+__use_tmux=0
+[ -n "$TMUX_PANE" -a ${FZF_TMUX:-1} -ne 0 -a ${LINES:-40} -gt 15 ] && __use_tmux=1
+
+if [ -z "$(set -o | \grep '^vi.*on')" ]; then
+ # Required to refresh the prompt after fzf
+ bind '"\er": redraw-current-line'
+
+ # CTRL-T - Paste the selected file path into the command line
+ if [ $__use_tmux -eq 1 ]; then
+ bind '"\C-t": " \C-u \C-a\C-k$(__fsel_tmux)\e\C-e\C-y\C-a\C-d\C-y\ey\C-h"'
+ else
+ bind '"\C-t": " \C-u \C-a\C-k$(__fsel)\e\C-e\C-y\C-a\C-y\ey\C-h\C-e\er \C-h"'
+ fi
+
+ # CTRL-R - Paste the selected command from history into the command line
+ bind '"\C-r": " \C-e\C-u$(HISTTIMEFORMAT= history | fzf +s --tac +m -n2..,.. | sed \"s/ *[0-9]* *//\")\e\C-e\er"'
+
+ # ALT-C - cd into the selected directory
+ bind '"\ec": " \C-e\C-u$(__fcd)\e\C-e\er\C-m"'
+else
+ bind '"\C-x\C-e": shell-expand-line'
+ bind '"\C-x\C-r": redraw-current-line'
+
+ # CTRL-T - Paste the selected file path into the command line
+ # - FIXME: Selected items are attached to the end regardless of cursor position
+ if [ $__use_tmux -eq 1 ]; then
+ bind '"\C-t": "\e$a \eddi$(__fsel_tmux)\C-x\C-e\e0P$xa"'
+ else
+ bind '"\C-t": "\e$a \eddi$(__fsel)\C-x\C-e\e0Px$a \C-x\C-r\exa "'
+ fi
+ bind -m vi-command '"\C-t": "i\C-t"'
+
+ # CTRL-R - Paste the selected command from history into the command line
+ bind '"\C-r": "\eddi$(HISTTIMEFORMAT= history | fzf +s --tac +m -n2..,.. | sed \"s/ *[0-9]* *//\")\C-x\C-e\e$a\C-x\C-r"'
+ bind -m vi-command '"\C-r": "i\C-r"'
+
+ # ALT-C - cd into the selected directory
+ bind '"\ec": "\eddi$(__fcd)\C-x\C-e\C-x\C-r\C-m"'
+ bind -m vi-command '"\ec": "i\ec"'
+fi
+
+unset __use_tmux
+
+fi
diff --git a/shell/key-bindings.fish b/shell/key-bindings.fish
new file mode 100644
index 00000000..be39e3d5
--- /dev/null
+++ b/shell/key-bindings.fish
@@ -0,0 +1,79 @@
+# Key bindings
+# ------------
+function fzf_key_bindings
+ # Due to a bug of fish, we cannot use command substitution,
+ # so we use temporary file instead
+ if [ -z "$TMPDIR" ]
+ set -g TMPDIR /tmp
+ end
+
+ function __fzf_list
+ command find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
+ -o -type f -print \
+ -o -type d -print \
+ -o -type l -print 2> /dev/null | sed 1d | cut -b3-
+ end
+
+ function __fzf_list_dir
+ command find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) \
+ -prune -o -type d -print 2> /dev/null | sed 1d | cut -b3-
+ end
+
+ function __fzf_escape
+ while read item
+ echo -n (echo -n "$item" | sed -E 's/([ "$~'\''([{<>})])/\\\\\\1/g')' '
+ end
+ end
+
+ function __fzf_ctrl_t
+ if [ -n "$TMUX_PANE" -a "$FZF_TMUX" != "0" ]
+ tmux split-window (__fzf_tmux_height) "fish -c 'fzf_key_bindings; __fzf_ctrl_t_tmux \\$TMUX_PANE'"
+ else
+ __fzf_list | fzf -m > $TMPDIR/fzf.result
+ and commandline -i (cat $TMPDIR/fzf.result | __fzf_escape)
+ commandline -f repaint
+ rm -f $TMPDIR/fzf.result
+ end
+ end
+
+ function __fzf_ctrl_t_tmux
+ __fzf_list | fzf -m > $TMPDIR/fzf.result
+ and tmux send-keys -t $argv[1] (cat $TMPDIR/fzf.result | __fzf_escape)
+ rm -f $TMPDIR/fzf.result
+ end
+
+ function __fzf_ctrl_r
+ history | fzf +s +m > $TMPDIR/fzf.result
+ and commandline (cat $TMPDIR/fzf.result)
+ commandline -f repaint
+ rm -f $TMPDIR/fzf.result
+ end
+
+ function __fzf_alt_c
+ # Fish hangs if the command before pipe redirects (2> /dev/null)
+ __fzf_list_dir | fzf +m > $TMPDIR/fzf.result
+ [ (cat $TMPDIR/fzf.result | wc -l) -gt 0 ]
+ and cd (cat $TMPDIR/fzf.result)
+ commandline -f repaint
+ rm -f $TMPDIR/fzf.result
+ end
+
+ function __fzf_tmux_height
+ if set -q FZF_TMUX_HEIGHT
+ set height $FZF_TMUX_HEIGHT
+ else
+ set height 40%
+ end
+ if echo $height | \grep -q -E '%$'
+ echo "-p "(echo $height | sed 's/%$//')
+ else
+ echo "-l $height"
+ end
+ set -e height
+ end
+
+ bind \ct '__fzf_ctrl_t'
+ bind \cr '__fzf_ctrl_r'
+ bind \ec '__fzf_alt_c'
+end
+
diff --git a/shell/key-bindings.zsh b/shell/key-bindings.zsh
new file mode 100644
index 00000000..84ba7302
--- /dev/null
+++ b/shell/key-bindings.zsh
@@ -0,0 +1,59 @@
+# Key bindings
+# ------------
+# CTRL-T - Paste the selected file path(s) into the command line
+__fsel() {
+ command find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
+ -o -type f -print \
+ -o -type d -print \
+ -o -type l -print 2> /dev/null | sed 1d | cut -b3- | fzf -m | while read item; do
+ printf '%q ' "$item"
+ done
+ echo
+}
+
+if [[ $- =~ i ]]; then
+
+if [ -n "$TMUX_PANE" -a ${FZF_TMUX:-1} -ne 0 -a ${LINES:-40} -gt 15 ]; then
+ fzf-file-widget() {
+ local height
+ height=${FZF_TMUX_HEIGHT:-40%}
+ if [[ $height =~ %$ ]]; then
+ height="-p ${height%\%}"
+ else
+ height="-l $height"
+ fi
+ tmux split-window $height "zsh -c 'source ~/.fzf.zsh; tmux send-keys -t $TMUX_PANE \"\$(__fsel)\"'"
+ }
+else
+ fzf-file-widget() {
+ LBUFFER="${LBUFFER}$(__fsel)"
+ zle redisplay
+ }
+fi
+zle -N fzf-file-widget
+bindkey '^T' fzf-file-widget
+
+# ALT-C - cd into the selected directory
+fzf-cd-widget() {
+ cd "${$(command find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
+ -o -type d -print 2> /dev/null | sed 1d | cut -b3- | fzf +m):-.}"
+ zle reset-prompt
+}
+zle -N fzf-cd-widget
+bindkey '\ec' fzf-cd-widget
+
+# CTRL-R - Paste the selected command from history into the command line
+fzf-history-widget() {
+ local selected
+ if selected=$(fc -l 1 | fzf +s --tac +m -n2..,.. -q "$LBUFFER"); then
+ num=$(echo "$selected" | head -1 | awk '{print $1}' | sed 's/[^0-9]//g')
+ LBUFFER=!$num
+ zle expand-history
+ fi
+ zle redisplay
+}
+zle -N fzf-history-widget
+bindkey '^R' fzf-history-widget
+
+fi
+