-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfzf
executable file
·215 lines (182 loc) · 6.41 KB
/
fzf
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#!/usr/bin/env sh
# vim: ft=sh ts=4 sw=4 sts=4 et :
# Hijack fzf command for extra functionality, including adaptive height,
# flexible preview window layout, and image preview with ueberzugpp
has() {
command -v "$1" >/dev/null 2>&1
}
# Get terminal size
lines=$(has tput && tput lines || echo "${LINES:-24}")
cols=$(has tput && tput cols || echo "${COLUMNS:-80}")
# Get cursor position or return 1;1 if not available
# Source:
# https://stackoverflow.com/questions/2575037/how-to-get-the-cursor-position-in-bash
cursorpos() {
# R/w to /dev/tty blocks in nvim's `jobstart()`
if [ -n "$NVIM" ]; then
echo '1;1'
return
fi
# Try opening /dev/tty, return fake cursor position if it fails
if ! { exec 3<>/dev/tty; } 2>/dev/null; then
echo '1;1'
return
fi
# Test reading from tty with timeout, return fake cursor position if timeout
read -t 0.1 -n 0 <>/dev/tty || {
exec 3>&-
echo '1;1'
return
}
exec 3>&-
# Now we can safely access /dev/tty
exec </dev/tty
oldstty=$(stty -g)
stty raw -echo min 0
printf '\033[6n' >/dev/tty
# Read cursor position response
pos=$(dd count=1 bs=32 2>/dev/null)
stty "$oldstty"
# Parse response of format "ESC[row;colR"
case "$pos" in
*'['*';'*R*)
row=${pos#*[}
row=${row%;*}
col=${pos#*;}
col=${col%R}
echo "$((row - 1));$((col - 1))"
;;
*)
echo '1;1'
;;
esac
}
# Normalize percentage to number
normalize_fzf_geometry() {
# $1: number or percentage
# $2: total
case "$1" in
*%) echo "$((${1%\%} * $2 / 100))" ;;
*) echo "$1" ;;
esac
}
# Get fzf option value
get_fzf_long_opt_no_space() {
# $1: long option name
# $2: default value of the option if not set
if ! has grep; then
echo "$2"
return 1
fi
val=$(printf '%s' "$FZF_DEFAULT_OPTS" | grep -o -- "--$1=[^ ]*" | tail -n1)
val=${val#--$1=}
val=${val#[\"\']}
val=${val%[\"\']}
[ -z "$val" ] && echo "$2" || echo "$val"
}
# Get fzf margin or padding from $FZF_DEFAULT_OPTS environment variable fzf
# exports $FZF_PREVIEW_{COLUMNS,LINES} so that previewer can adjust its size
# accordingly but information about padding and margin is not provided, so we
# have to calculate it according to $FZF_DEFAULT_OPTS, this is not ideal since
# it won't work if user overrides the preview window settings in $@
get_fzf_geometry_normalized() {
# $1: 'margin' or 'padding'
# $2: 'top', 'bottom', 'left', or 'right'
geometry=$(get_fzf_long_opt_no_space "$1" 0)
[ $? -ne 0 ] && echo "$geometry" && return 1
case "$2" in
top | bottom) total=$lines ;;
*) total=$cols ;;
esac
case "$geometry" in
[0-9]*% | [0-9]*)
normalize_fzf_geometry "$geometry" "$total"
;;
[0-9]*%,[0-9]*% | [0-9]*,[0-9]*)
case "$2" in
top | bottom) val=${geometry%%,*} ;;
*) val=${geometry##*,} ;;
esac
normalize_fzf_geometry "$val" "$total"
;;
*)
echo 0
;;
esac
}
main() {
# Create temporary directory
tmp_dir=$(mktemp -d -t "$(basename "$0").XXXXXXXX")
[ ! -d "$tmp_dir" ] && echo 'Failed to create temp dir' >&2 && return 1
trap 'rm -rf "$tmp_dir"' HUP INT QUIT ABRT EXIT
# Link files to avoid recursion
script_dir=$(dirname "$0")
for file in "$script_dir"/*; do
[ "$file" = "$0" ] && continue
ln -s "$file" "$tmp_dir"
done
# Update PATH
PATH=$(printf '%s' "$PATH" | tr ':' '\n' | sed "s|^$script_dir\$|$tmp_dir|" | tr '\n' ':' | sed 's/:$//')
if ! has fzf; then
echo 'fzf not found' >&2
return 1
fi
# Calculate adaptive height
cursor=$(cursorpos)
cursor_row=${cursor%;*}
fzf_height=$((lines - cursor_row - 1))
min_height=$(get_fzf_long_opt_no_space min-height 10)
[ "$fzf_height" -lt "$min_height" ] && fzf_height=$min_height
[ "$fzf_height" -gt "$lines" ] && fzf_height=$lines
opts="--height=$fzf_height"
# Calculate preview window layout
margin_top=$(get_fzf_geometry_normalized margin top)
margin_right=$(get_fzf_geometry_normalized margin right)
margin_bottom=$(get_fzf_geometry_normalized margin bottom)
margin_left=$(get_fzf_geometry_normalized margin left)
padding_top=$(get_fzf_geometry_normalized padding top)
padding_right=$(get_fzf_geometry_normalized padding right)
padding_bottom=$(get_fzf_geometry_normalized padding bottom)
padding_left=$(get_fzf_geometry_normalized padding left)
inner_height=$((fzf_height - margin_top - margin_bottom - padding_top - padding_bottom))
inner_width=$((cols - margin_left - margin_right - padding_left - padding_right))
[ "$inner_height" -lt 1 ] && inner_height=1
[ "$inner_width" -lt 1 ] && inner_width=1
if [ "$inner_height" -gt 16 ] || [ "$inner_width" -gt 48 ]; then
if [ "$inner_width" -gt $((inner_height * 3)) ] && [ "$inner_width" -ge 80 ]; then
preview_window_orientation='right'
else
preview_window_orientation='down'
fi
opts="$opts --preview-window=$preview_window_orientation"
else
opts="$opts --no-preview"
preview_disabled=true
fi
# Setup ueberzugpp if available
if has ueberzugpp && [ "$FZF_PREVIEW_DISABLE_UB" != 'true' ] && [ "$preview_disabled" != 'true' ]; then
ub_pid_file="/tmp/.$(uuidgen)"
ueberzugpp layer --silent --no-stdin --use-escape-codes --pid-file "$ub_pid_file" >/dev/null 2>&1
# Export variables for previewer
UB_SOCK="/tmp/ueberzugpp-$(cat "$ub_pid_file").socket"
export UB_SOCK
export FZF_PREVIEW_ORIENTATION="$preview_window_orientation"
export FZF_HEIGHT="$fzf_height"
export FZF_MARGIN_TOP="$margin_top"
export FZF_MARGIN_RIGHT="$margin_right"
export FZF_MARGIN_BOTTOM="$margin_bottom"
export FZF_MARGIN_LEFT="$margin_left"
export FZF_PADDING_TOP="$padding_top"
export FZF_PADDING_RIGHT="$padding_right"
export FZF_PADDING_BOTTOM="$padding_bottom"
export FZF_PADDING_LEFT="$padding_left"
export FZF_TERM_LINES="$lines"
export FZF_TERM_COLUMNS="$cols"
fi
# Run fzf with our options
FZF_DEFAULT_OPTS="$FZF_DEFAULT_OPTS $opts" fzf "$@"
# Cleanup ueberzugpp
[ -n "$UB_SOCK" ] && ueberzugpp cmd -s "$UB_SOCK" -a exit &
[ -e "$ub_pid_file" ] && rm -f "$ub_pid_file" &
}
main "$@"