-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathzsh-interactive-vim.plugin.zsh
146 lines (130 loc) · 6.03 KB
/
zsh-interactive-vim.plugin.zsh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/env zsh
__ziv_matched_subdir_list() {
local dir length seg starts_with_dir
if [[ "$1" == */ ]]; then # arg ends with /
dir="$1"
[[ "$dir" != / ]] && dir="${dir%?}" # remove trailing / (unless root dir)
length=$(echo -n "$dir" | wc -c) # dir length stored in length (-n for no newline)
[[ "$dir" == / ]] && length=0 # account for root edge case
find -L "$dir" -maxdepth 1 2>/dev/null | cut -b $(( $length + 2 ))- | sed '/^$/d' | while read -r line; do #find subdirs/files at depth one, cut initial "./", delete empty paths and iterate over them
echo $line
done
else
dir=$(dirname -- "$1") # parent dir (a/b/c -> b; a/b/ -> a)
length=$(echo -n "$dir" | wc -c) # dir length stored in length
[[ "$dir" == / ]] && length=0 # if parent dir is root dirname outputs /
seg=$(basename -- "$1") # name of the file/dir at the end (might be incomplete)
[[ "$seg" == "." && $(find -L . -maxdepth 1 2>/dev/null | grep "^\./\." | wc -l) -eq 0 ]] && seg="" # search for dirs in . (not starting with .) in case there aren't hidden dirs
matching_dirs=$(find -L "$dir" -maxdepth 1 2>/dev/null | cut -b $(( $length + 2 ))- | sed '/^$/d' | while read -r line; do
[[ "${seg[1]}" != "." && "${line[1]}" == "." ]] && continue # ignore dirs starting with.
if [ "$ziv_case_insensitive" = "true" ]; then
[[ "$line:u" == "$seg:u"* ]] && echo "$line"
else
[[ "$line" == "$seg"* ]] && echo "$line"
fi
done) # directories starting with seg directly in current dir
[ -n "$matching_dirs" ] && echo "$matching_dirs"
fi
}
__ziv_fzf_bindings() {
autoload is-at-least
if $(is-at-least '0.21.0' $(fzf --version)); then
echo 'shift-tab:up,tab:down,bspace:backward-delete-char/eof' # enable backspace to go out of fzf
else
echo 'shift-tab:up,tab:down'
fi
}
__ziv_empty_arg() {
if [[ $LBUFFER =~ .*\ $ ]]; then
echo "1"
else
echo "0"
fi
}
__ziv_current_arg() {
if [[ $(__ziv_empty_arg) -eq 1 ]]; then
echo ""
else
echo '${(Q)@[-1]}'
fi
}
_ziv_list_generator() {
__ziv_matched_subdir_list $(eval echo $(__ziv_current_arg)) | sort #call __ziv_matched_subdir_list on the last [-1] arg unqouted (--flags "path to/dir" -> path to/dir )
}
_ziv_preview() {
local _base base current dir
_base=(${(z)1})
base=${_base[-1]}
[[ $(__ziv_empty_arg) -eq 1 ]] && base="." # fix empty arg
[[ "$base" != */ ]] && base=$(dirname -- $base) # remove partial dir/filename
[[ "$base" == */ ]] && base="${base%?}" # remove trailing /
current=$2
dir="$base/$2"
export CLICOLOR_FORCE=1 # force colors
if [ -d "$dir" ]; then
[ -z "$ziv_dir_preview" ] && ziv_dir_preview="ls"
[ -z "$ziv_dir_preview_flags" ] && ziv_dir_preview_flags=()
$ziv_dir_preview ${ziv_dir_preview_flags[@]} $dir
else
[ -z "$ziv_file_preview" ] && ziv_file_preview="bat"
[ -z "$ziv_file_preview_flags" ] && ziv_file_preview_flags=("--color=always")
$ziv_file_preview ${ziv_file_preview_flags[@]} $dir
fi
}
_ziv_complete() { # perform completion
setopt localoptions nonomatch
local l matches fzf tokens base
l=$(_ziv_list_generator $@)
if [ -z $l ]; then
zle ${__ziv_default_completion:-expand-or-complete}
return
fi
fzf_bindings=$(__ziv_fzf_bindings)
if [ $(echo $l | wc -l) -eq 1 ]; then
matches=${(q)l} # match the only element in the list (special chars accounted for (q))
else
SHELL=$(which zsh) matches=$(echo $l | __ziv_preview=$(declare -f _ziv_preview) _base=$* ___ziv_empty_arg=$(declare -f __ziv_empty_arg) LBUFFER=$LBUFFER FZF_DEFAULT_OPTS="--height 40% --bind '$fzf_bindings' --reverse" fzf --preview 'eval $__ziv_preview;eval $___ziv_empty_arg; _ziv_preview "$_base" {}' | while read -r item; do
echo -n "${(q)item} "
done)
fi
matches=${matches% } # remove trailing whitespace
if [ -n "$matches" ]; then
tokens=(${(z)LBUFFER})
base="$(eval echo $(__ziv_current_arg))" # path, unquoted
if [[ "$base" != */ ]]; then # if path not /-finished
if [[ "$base" == */* ]]; then # path contains /
base="$(dirname -- "$base")"
[[ ${base[-1]} != / ]] && base="$base/"
else
base="" # relative to current, don't include /
fi
fi
LBUFFER="${tokens[1,$(( ${#tokens} - 1 + $(__ziv_empty_arg) ))]} " # LBUFFER are characters to the left of the cursor in ZSH - initially set to cmd (*vim) + arg + args
if [ -n "$base" ]; then
base=${(q)base}
LBUFFER="$LBUFFER${base}" # append formatted base (special chars)
fi
LBUFFER="$LBUFFER$matches" # append selected dir/file
[ -d "${base}$matches" ] && LBUFFER="$LBUFFER/" # append trailing / if dir
fi
zle redisplay
declare -f zle-line-init > /dev/null && zle zle-line-init # check if zle-line-init widget is defined, zle-line-init if so
}
ziv-completion() { # zle widget, match "*vim " and execute _ziv_complete, fallback to default completion otherwise
setopt localoptions noshwordsplit noksh_arrays noposixbuiltins
local tokens cmd
tokens=(${(z)LBUFFER}) # parens exclude whitespaces from the array
cmd=${tokens[1]}
if [[ "$LBUFFER" =~ "^\ *${ziv_regex:-.?vim}$" ]]; then # match vim and nvim when cursor is right after them, matching any amount of spaces before
zle ${__ziv_default_completion:-expand-or-complete}
elif [[ "$cmd" =~ "${ziv_regex:-.?vim}" ]]; then
_ziv_complete ${tokens[2,${#tokens}]/#\~/$HOME} # perform actual completion (send array of args replacing ~ with $HOME)
else
zle ${__ziv_default_completion:-expand-or-complete}
fi
}
zle -N ziv-completion # register as a macro
ziv_currently_bound=$(bindkey ${ziv_custom_keybind:-'^I'} | awk '{print $2}')
[[ "$ziv_currently_bound" != "ziv-completion" ]] && __ziv_default_completion=$ziv_currently_bound # set fallback to what was bound to the keybinding beforehand (ensure compatibility with plugins like zsh-interactive-cd as long as this is enabled afterwards) (check to prevent infinite recursion)
[ -z $ziv_custom_binding ] && ziv_custom_binding='^I' # default to ^I/TAB for completion
bindkey $ziv_custom_binding ziv-completion # bind macro