Skip to content

Commit

Permalink
Even faster completion over SSH
Browse files Browse the repository at this point in the history
  • Loading branch information
mjuric committed Dec 23, 2019
1 parent f0ed68d commit 6debb46
Showing 1 changed file with 59 additions and 39 deletions.
98 changes: 59 additions & 39 deletions git-clone-completions.bash
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,7 @@ __mj_ssh_write()
# sentinel is encountered.
__mj_ssh_read()
{
local IFS=$'\n'
while read -r line <&218; do
if [[ $line == "${__ssh_msg_sentinel}" ]]; then
return
Expand Down Expand Up @@ -879,60 +880,79 @@ _ssh()
__mj_ssh_read || { _dbg "read failed"; __mj_ssh_stop; return 1; }
}

_new_test()
{
local userhost=${1%%:*}
local path=${1#*:}

# things we want to backslash escape in scp paths
_scp_path_esc='[][(){}<>",:;^&!$=?`|\\'"'"'[:space:]]'
local dir pfix
[[ "$path" == */ ]] && { dir="$path"; pfix=; } || { dir=$(dirname $path); pfix=$(basename $path); }

# Complete remote files with ssh. If the first arg is -d, complete on dirs
# only. Returns paths escaped with three backslashes.
_ssh_list_files()
{
local cmd;
read -r -d '' cmd <<-EOF
cd $dir && (
ls -aF1dL $pfix*/HEAD | sed -n 's|/HEAD[^/]*$||p';
ls -aF1dL $pfix*/.git | sed -n 's|/.git/$||p';
ls -aF1dL $pfix* | sed -n 's|/$||p'
) 2>/dev/null | sort | uniq -c | sed -nE 's| *2 (.*)|\1 |p; s| *1 (.*)|\1/|p'
EOF

echo "$cmd"

# grab all
local IFS=$'\n'
[[ $dir == "." ]] && dir=
echo "dir=$dir"
local res=$(_ssh "$userhost" "$cmd" | sed 's|^|'"$dir"'|')
echo "$res"
}

# should we only return dirs?
local dirs_only
[[ $1 == -d ]] && { dirs_only=1; shift; }
# Find completions for <fragment> on <host>, return them in ${COMPREPLY[@]}
#
# _ssh_list_repos <url>:<fragment>
#
# Once the initial SSH connection is established, this is typically fast
# (on order of 50msec, depending on the speed of your server.)
#
_ssh_list_repos()
{
local IFS=$'\n'

# split url
local userhost=${1%%:*}
local path=${1#*:}
local sockdir="${2:-$HOME/.ssh}"

#_dbg "cur=[$1] userhost=[$userhost] path=[$path] sockdir=[$sockdir]"

# prime the cached connection (as all subsequent invocations will be
# in subshells and can't set the various __ssh_* variables with
# connection reuse info)
_ssh_ensure_started "$userhost" &>/dev/null

local files
if [[ $dirs_only == 1 ]]; then
# escape problematic characters; remove non-dirs
files=$(_ssh "$userhost" \
command ls -aF1dL "$path*" 2>/dev/null | \
command sed -e 's/'$_scp_path_esc'/\\&/g' -e '/[^\/]$/d')

# if only one dir remains, and it has no subdirs, append a space
# rather than a slash
local f=( $files )
if [[ ${#f[@]} == 1 ]]; then
#_dbg "checking subdir"
if [[ -z $(_ssh "$userhost" command ls -aF1dL "$f*" 2>/dev/null | sed -e '/[^\/]$/d' ) ]]; then
files="${f%/} "
#_dbg "end of hierarchy: files=[[$files]]"
fi
fi
else
# escape problematic characters; remove executables, aliases, pipes
# and sockets; add space at end of file names
files=$(_ssh "$userhost" \
command ls -aF1dL "$path*" 2>/dev/null | \
command sed -e 's/'$_scp_path_esc'/\\&/g' -e 's/[*@|=]$//g' \
-e 's/[^\/]$/& /g')
fi
# here we construct a list of directories containing git repositories
# in addition to the list of _all_ directories. The algorithm:
#
# 1. list directories with HEAD (bare git dirs) and .git/ (workdirs)
# 2. list all directories
# 3. merge the above lists, sort them, and run unique -c which prints
# out the number of times an entry has appeared. the directories
# which are git dirs will have appeared _twice_ (once because they
# were picked up by #1 above, once because of #2.
# 4. for each dir that appeared twice, append a ' ' to the end signaling
# the end of completion. otherwise, append a '/'.
local cmd
read -r -d '' cmd <<-EOF
(
ls -aF1dL $path*/HEAD | sed -n 's|/HEAD[^/]*$||p';
ls -aF1dL $path*/.git | sed -n 's|/.git/$||p';
ls -aF1dL $path* | sed -n 's|/$||p'
) 2>/dev/null | sort | uniq -c | sed -nE 's| *2 (.*)|\1 |p; s| *1 (.*)|\1/|p'
EOF
#echo "$cmd"

# grab all
local files=$(_ssh "$userhost" "$cmd")

COMPREPLY=($files)
#echo "$files"
#echo "===$files==="
}

# test if we're completing a generic SSH URL, complete it if so, return 1
Expand Down Expand Up @@ -963,7 +983,7 @@ _complete_ssh_url()

# autocomplete the path
start_spinner
_ssh_list_files -d "$cur"
_ssh_list_repos "$cur"
stop_spinner

# remember last few successful completions to offer to
Expand Down

0 comments on commit 6debb46

Please sign in to comment.