Skip to content

Commit f87ff67

Browse files
LoricAndreSkim bot
andauthored
feat(shell): readd completions (#726) (#739)
* feat(shell): readd completions (#726) * chore: generate completions & manpage * chore(shell): force reset colors & cursor * docs(shell): add docs for shell modules (closes #315) * chore: generate completions & manpage * docs(shell): add docs on how to enable (closes #292) --------- Co-authored-by: LoricAndre <[email protected]> Co-authored-by: Skim bot <[email protected]>
1 parent 1952435 commit f87ff67

File tree

5 files changed

+586
-19
lines changed

5 files changed

+586
-19
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ skim provides a single executable: `sk`. Anywhere you would want to use
3232
* [As Vim plugin](#as-vim-plugin)
3333
* [As filter](#as-filter)
3434
* [As Interactive Interface](#as-interactive-interface)
35+
* [Shell Bindings](#shell-bindings)
3536
* [Key Bindings](#key-bindings)
3637
* [Search Syntax](#search-syntax)
3738
* [exit code](#exit-code)
@@ -159,6 +160,18 @@ sk --ansi -i -c 'rg --color=always --line-number "{}"'
159160
160161
![interactive mode demo](https://cloud.githubusercontent.com/assets/1527040/21603930/655d859a-d1db-11e6-9fec-c25099d30a12.gif)
161162
163+
## Shell Bindings
164+
165+
Bindings for Fish, Bash and Zsh are available in the `shell` directory:
166+
- `completion.{shell}` contains the completion scripts for `sk` cli usage
167+
- `key-bindings.{shell}` contains key-binds and shell integrations:
168+
- `ctrl-t` to select a file through `sk`
169+
- `ctrl-r` to select an history entry through `sk`
170+
- `alt-c` to `cd` into a directory selected through `sk`
171+
- (not available in `fish`) `**` to complete file paths, for example `ls **<tab>` will show a `sk` widget to select a folder
172+
173+
To enable them, you need to source the `key-bindings.{shell}` file and enable the completions according to your shell's documentation (usually by placing them in the right directory).
174+
162175
## Key Bindings
163176
164177
Some commonly used key bindings:

shell/key-bindings.bash

Lines changed: 293 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
# ____ ____
2-
# / __/___ / __/
3-
# / /_/_ / / /_
4-
# / __/ / /_/ __/
5-
# /_/ /___/_/ key-bindings.bash
6-
#
71
# - $SKIM_TMUX_OPTS
82
# - $SKIM_CTRL_T_COMMAND
93
# - $SKIM_CTRL_T_OPTS
104
# - $SKIM_CTRL_R_OPTS
115
# - $SKIM_ALT_C_COMMAND
126
# - $SKIM_ALT_C_OPTS
7+
# - $SKIM_COMPLETION_TRIGGER (default: '**')
8+
# - $SKIM_COMPLETION_OPTS (default: empty)
139

1410
# Key bindings
1511
# ------------
@@ -27,9 +23,10 @@ __skim_select__() {
2723

2824
if [[ $- =~ i ]]; then
2925

26+
3027
__skimcmd() {
3128
[ -n "$TMUX_PANE" ] && { [ "${SKIM_TMUX:-0}" != 0 ] || [ -n "$SKIM_TMUX_OPTS" ]; } &&
32-
echo "sk-tmux ${SKIM_TMUX_OPTS:--d${SKIM_TMUX_HEIGHT:-40%}} -- " || echo "sk"
29+
echo "sk --tmux=${SKIM_TMUX_OPTS:-center,${SKIM_TMUX_HEIGHT:-40%}} -- " || echo "sk"
3330
}
3431

3532
skim-file-widget() {
@@ -42,7 +39,8 @@ __skim_cd__() {
4239
local cmd dir
4340
cmd="${SKIM_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
4441
-o -type d -print 2> /dev/null | cut -b3-"}"
45-
dir=$(eval "$cmd" | SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} --reverse $SKIM_DEFAULT_OPTIONS $SKIM_ALT_C_OPTS" $(__skimcmd) --no-multi) && printf 'cd %q' "$dir"
42+
dir=$(eval "$cmd" | SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} --reverse $SKIM_DEFAULT_OPTIONS $SKIM_ALT_C_OPTS" $(__skimcmd) --no-multi)
43+
printf 'cd %q' "$dir"
4644
}
4745

4846
__skim_history__() {
@@ -52,6 +50,7 @@ __skim_history__() {
5250
last_hist=$(HISTTIMEFORMAT='' builtin history 1) perl -n -l0 -e 'BEGIN { getc; $/ = "\n\t"; $HISTCMD = $ENV{last_hist} + 1 } s/^[ *]//; print $HISTCMD - $. . "\t$_" if !$seen{$_}++' |
5351
SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} $SKIM_DEFAULT_OPTIONS -n2..,.. --bind=ctrl-r:toggle-sort $SKIM_CTRL_R_OPTS --no-multi --read0" $(__skimcmd) --query "$READLINE_LINE"
5452
) || return
53+
echo -e "\033[0m"
5554
READLINE_LINE=${output#*$'\t'}
5655
if [ -z "$READLINE_POINT" ]; then
5756
echo "$READLINE_LINE"
@@ -94,4 +93,290 @@ bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__skim_cd__`\e\C-e\er\C-m\C-y\C-h
9493
bind -m vi-command '"\ec": "\C-z\ec\C-z"'
9594
bind -m vi-insert '"\ec": "\C-z\ec\C-z"'
9695

96+
# Completion
97+
98+
if ! declare -f _skim_compgen_path > /dev/null; then
99+
_skim_compgen_path() {
100+
echo "$1"
101+
command find -L "$1" \
102+
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
103+
-a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
104+
}
105+
fi
106+
107+
if ! declare -f _skim_compgen_dir > /dev/null; then
108+
_skim_compgen_dir() {
109+
command find -L "$1" \
110+
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
111+
-a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
112+
}
113+
fi
114+
115+
###########################################################
116+
117+
# To redraw line after skim closes (printf '\e[5n')
118+
bind '"\e[0n": redraw-current-line'
119+
120+
__skim_comprun() {
121+
if [ "$(type -t _skim_comprun 2>&1)" = function ]; then
122+
_skim_comprun "$@"
123+
elif [ -n "$TMUX_PANE" ] && { [ "${SKIM_TMUX:-0}" != 0 ] || [ -n "$SKIM_TMUX_OPTS" ]; }; then
124+
shift
125+
sk --tmux=${SKIM_TMUX_OPTS:-center,${SKIM_TMUX_HEIGHT:-40%}} -- "$@"
126+
else
127+
shift
128+
sk "$@"
129+
fi
130+
}
131+
132+
__skim_orig_completion_filter() {
133+
sed 's/^\(.*-F\) *\([^ ]*\).* \([^ ]*\)$/export _skim_orig_completion_\3="\1 %s \3 #\2"; [[ "\1" = *" -o nospace "* ]] \&\& [[ ! "$__skim_nospace_commands" = *" \3 "* ]] \&\& __skim_nospace_commands="$__skim_nospace_commands \3 ";/' |
134+
awk -F= '{OFS = FS} {gsub(/[^A-Za-z0-9_= ;]/, "_", $1);}1'
135+
}
136+
137+
_skim_handle_dynamic_completion() {
138+
local cmd orig_var orig ret orig_cmd orig_complete
139+
cmd="$1"
140+
shift
141+
orig_cmd="$1"
142+
orig_var="_skim_orig_completion_$cmd"
143+
orig="${!orig_var##*#}"
144+
if [ -n "$orig" ] && type "$orig" > /dev/null 2>&1; then
145+
$orig "$@"
146+
elif [ -n "$_skim_completion_loader" ]; then
147+
orig_complete=$(complete -p "$orig_cmd" 2> /dev/null)
148+
_completion_loader "$@"
149+
ret=$?
150+
# _completion_loader may not have updated completion for the command
151+
if [ "$(complete -p "$orig_cmd" 2> /dev/null)" != "$orig_complete" ]; then
152+
eval "$(complete | command grep " -F.* $orig_cmd$" | __skim_orig_completion_filter)"
153+
if [[ "$__skim_nospace_commands" = *" $orig_cmd "* ]]; then
154+
eval "${orig_complete/ -F / -o nospace -F }"
155+
else
156+
eval "$orig_complete"
157+
fi
158+
fi
159+
return $ret
160+
fi
161+
}
162+
163+
__skim_generic_path_completion() {
164+
local cur base dir leftover matches trigger cmd
165+
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
166+
COMPREPLY=()
167+
trigger=${SKIM_COMPLETION_TRIGGER-'**'}
168+
cur="${COMP_WORDS[COMP_CWORD]}"
169+
if [[ "$cur" == *"$trigger" ]]; then
170+
base=${cur:0:${#cur}-${#trigger}}
171+
eval "base=$base"
172+
173+
[[ $base = *"/"* ]] && dir="$base"
174+
while true; do
175+
if [ -z "$dir" ] || [ -d "$dir" ]; then
176+
leftover=${base/#"$dir"}
177+
leftover=${leftover/#\/}
178+
[ -z "$dir" ] && dir='.'
179+
[ "$dir" != "/" ] && dir="${dir/%\//}"
180+
matches=$(eval "$1 $(printf %q "$dir")" | SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} --reverse $SKIM_DEFAULT_OPTIONS $SKIM_COMPLETION_OPTS $2" __skim_comprun "$4" -q "$leftover" | while read -r item; do
181+
printf "%q$3 " "$item"
182+
done)
183+
matches=${matches% }
184+
[[ -z "$3" ]] && [[ "$__skim_nospace_commands" = *" ${COMP_WORDS[0]} "* ]] && matches="$matches "
185+
if [ -n "$matches" ]; then
186+
COMPREPLY=( "$matches" )
187+
else
188+
COMPREPLY=( "$cur" )
189+
fi
190+
printf '\e[5n'
191+
return 0
192+
fi
193+
dir=$(dirname "$dir")
194+
[[ "$dir" =~ /$ ]] || dir="$dir"/
195+
done
196+
else
197+
shift
198+
shift
199+
shift
200+
_skim_handle_dynamic_completion "$cmd" "$@"
201+
fi
202+
}
203+
204+
_skim_complete() {
205+
# Split arguments around --
206+
local args rest str_arg i sep
207+
args=("$@")
208+
sep=
209+
for i in "${!args[@]}"; do
210+
if [[ "${args[$i]}" = -- ]]; then
211+
sep=$i
212+
break
213+
fi
214+
done
215+
if [[ -n "$sep" ]]; then
216+
str_arg=
217+
rest=("${args[@]:$((sep + 1)):${#args[@]}}")
218+
args=("${args[@]:0:$sep}")
219+
else
220+
str_arg=$1
221+
args=()
222+
shift
223+
rest=("$@")
224+
fi
225+
226+
local cur selected trigger cmd post
227+
post="$(caller 0 | awk '{print $2}')_post"
228+
type -t "$post" > /dev/null 2>&1 || post=cat
229+
230+
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
231+
trigger=${SKIM_COMPLETION_TRIGGER-'**'}
232+
cur="${COMP_WORDS[COMP_CWORD]}"
233+
if [[ "$cur" == *"$trigger" ]]; then
234+
cur=${cur:0:${#cur}-${#trigger}}
235+
236+
selected=$(SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} --reverse $SKIM_DEFAULT_OPTIONS $SKIM_COMPLETION_OPTS $str_arg" __skim_comprun "${rest[0]}" "${args[@]}" -q "$cur" | $post | tr '\n' ' ')
237+
selected=${selected% } # Strip trailing space not to repeat "-o nospace"
238+
if [ -n "$selected" ]; then
239+
COMPREPLY=("$selected")
240+
else
241+
COMPREPLY=("$cur")
242+
fi
243+
printf '\e[5n'
244+
echo -e "\033[0m"
245+
return 0
246+
else
247+
_skim_handle_dynamic_completion "$cmd" "${rest[@]}"
248+
fi
249+
}
250+
251+
_skim_path_completion() {
252+
__skim_generic_path_completion _skim_compgen_path "-m" "" "$@"
253+
}
254+
255+
# Deprecated. No file only completion.
256+
_skim_file_completion() {
257+
_skim_path_completion "$@"
258+
}
259+
260+
_skim_dir_completion() {
261+
__skim_generic_path_completion _skim_compgen_dir "" "/" "$@"
262+
}
263+
264+
_skim_complete_kill() {
265+
local trigger=${SKIM_COMPLETION_TRIGGER-'**'}
266+
local cur="${COMP_WORDS[COMP_CWORD]}"
267+
if [[ -z "$cur" ]]; then
268+
COMP_WORDS[$COMP_CWORD]=$trigger
269+
elif [[ "$cur" != *"$trigger" ]]; then
270+
return 1
271+
fi
272+
273+
_skim_proc_completion "$@"
274+
}
275+
276+
_skim_proc_completion() {
277+
_skim_complete -m --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- "$@" < <(
278+
command ps -ef | sed 1d
279+
)
280+
}
281+
282+
_skim_proc_completion_post() {
283+
awk '{print $2}'
284+
}
285+
286+
_skim_host_completion() {
287+
_skim_complete --no-multi -- "$@" < <(
288+
command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
289+
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
290+
<(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
291+
awk '{if (length($2) > 0) {print $2}}' | sort -u
292+
)
293+
}
294+
295+
_skim_var_completion() {
296+
_skim_complete -m -- "$@" < <(
297+
declare -xp | sed 's/=.*//' | sed 's/.* //'
298+
)
299+
}
300+
301+
_skim_alias_completion() {
302+
_skim_complete -m -- "$@" < <(
303+
alias | sed 's/=.*//' | sed 's/.* //'
304+
)
305+
}
306+
307+
d_cmds="${SKIM_COMPLETION_DIR_COMMANDS:-cd pushd rmdir}"
308+
a_cmds="
309+
awk cat diff diff3
310+
emacs emacsclient ex file ftp g++ gcc gvim head hg java
311+
javac ld less more mvim nvim patch perl python ruby
312+
sed sftp sort source tail tee uniq vi view vim wc xdg-open
313+
basename bunzip2 bzip2 chmod chown curl cp dirname du
314+
find git grep gunzip gzip hg jar
315+
ln ls mv open rm rsync scp
316+
svn tar unzip zip"
317+
318+
# Preserve existing completion
319+
eval "$(complete |
320+
sed -E '/-F/!d; / _skim/d; '"/ ($(echo $d_cmds $a_cmds | sed 's/ /|/g; s/+/\\+/g'))$/"'!d' |
321+
__skim_orig_completion_filter)"
322+
323+
if type _completion_loader > /dev/null 2>&1; then
324+
_skim_completion_loader=1
325+
fi
326+
327+
__skim_defc() {
328+
local cmd func opts orig_var orig def
329+
cmd="$1"
330+
func="$2"
331+
opts="$3"
332+
orig_var="_skim_orig_completion_${cmd//[^A-Za-z0-9_]/_}"
333+
orig="${!orig_var}"
334+
if [ -n "$orig" ]; then
335+
printf -v def "$orig" "$func"
336+
eval "$def"
337+
else
338+
complete -F "$func" $opts "$cmd"
339+
fi
340+
}
341+
342+
# Anything
343+
for cmd in $a_cmds; do
344+
__skim_defc "$cmd" _skim_path_completion "-o default -o bashdefault"
345+
done
346+
347+
# Directory
348+
for cmd in $d_cmds; do
349+
__skim_defc "$cmd" _skim_dir_completion "-o nospace -o dirnames"
350+
done
351+
352+
# Kill completion (supports empty completion trigger)
353+
complete -F _skim_complete_kill -o default -o bashdefault kill
354+
355+
unset cmd d_cmds a_cmds
356+
357+
_skim_setup_completion() {
358+
local kind fn cmd
359+
kind=$1
360+
fn=_skim_${1}_completion
361+
if [[ $# -lt 2 ]] || ! type -t "$fn" > /dev/null; then
362+
echo "usage: ${FUNCNAME[0]} path|dir|var|alias|host|proc COMMANDS..."
363+
return 1
364+
fi
365+
shift
366+
eval "$(complete -p "$@" 2> /dev/null | grep -v "$fn" | __skim_orig_completion_filter)"
367+
for cmd in "$@"; do
368+
case "$kind" in
369+
dir) __skim_defc "$cmd" "$fn" "-o nospace -o dirnames" ;;
370+
var) __skim_defc "$cmd" "$fn" "-o default -o nospace -v" ;;
371+
alias) __skim_defc "$cmd" "$fn" "-a" ;;
372+
*) __skim_defc "$cmd" "$fn" "-o default -o bashdefault" ;;
373+
esac
374+
done
375+
}
376+
377+
# Environment variables / Aliases / Hosts
378+
_skim_setup_completion 'var' export unset
379+
_skim_setup_completion 'alias' unalias
380+
_skim_setup_completion 'host' ssh telnet
381+
97382
fi

shell/key-bindings.fish

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
11
#!/bin/fish
2-
# completion.fish
3-
# copied and modified from https://github.com/junegunn/fzf/blob/master/shell/key-bindings.fish
4-
# ____ ____
5-
# / __/___ / __/
6-
# / /_/_ / / /_
7-
# / __/ / /_/ __/
8-
# /_/ /___/_/ key-bindings.fish
9-
#
102
# - $SKIM_TMUX_OPTS
113
# - $SKIM_CTRL_T_COMMAND
124
# - $SKIM_CTRL_T_OPTS
135
# - $SKIM_CTRL_R_OPTS
146
# - $SKIM_ALT_C_COMMAND
157
# - $SKIM_ALT_C_OPTS
8+
# - $SKIM_COMPLETION_TRIGGER (default: '**')
9+
# - $SKIM_COMPLETION_OPTS (default: empty)
1610

1711
# Key bindings
1812
# ------------
@@ -160,5 +154,4 @@ function skim_key_bindings
160154

161155
echo $dir
162156
end
163-
164157
end

0 commit comments

Comments
 (0)