diff --git a/bash_completion b/bash_completion index 53eea33..e1e926b 100644 --- a/bash_completion +++ b/bash_completion @@ -618,7 +618,7 @@ _filedir() { local i IFS=$'\t\n' xspec - __expand_tilde_by_ref cur + _tilde "$cur" || return 0 local -a toks local quoted tmp @@ -803,7 +803,26 @@ _available_interfaces() } +# Perform tilde (~) completion +# @return True (0) if completion needs further processing, +# False (> 0) if tilde is followed by a valid username, completions +# are put in COMPREPLY and no further processing is necessary. +_tilde() { + local result=0 + # Does $1 start with tilde (~) and doesn't contain slash (/)? + if [[ ${1:0:1} == "~" && $1 == ${1//\/} ]]; then + # Try generate username completions + COMPREPLY=( $( compgen -P '~' -u "${1#\~}" ) ) + result=${#COMPREPLY[@]} + fi + return $result +} + + # Expand variable starting with tilde (~) +# We want to expand ~foo/... to /home/foo/... to avoid problems when +# word-to-complete starting with a tilde is fed to commands and ending up +# quoted instead of expanded. # Only the first portion of the variable from the tilde up to the first slash # (~../) is expanded. The remainder of the variable, containing for example # a dollar sign variable ($) or asterisk (*) is not expanded. diff --git a/test/lib/completions/ls.exp b/test/lib/completions/ls.exp index 171f6e1..fa47f85 100644 --- a/test/lib/completions/ls.exp +++ b/test/lib/completions/ls.exp @@ -19,4 +19,15 @@ if {[assert_exec {ls --help} "" "" "unsupported"]} { sync_after_int +set test "~part should complete to ~full" +assert_bash_exec {compgen -u} {} /@ users +find_unique_completion_pair $users part full +# If home directory exists, append slash "/", else space " " +set trail [expr {[llength [glob -nocomplain ~$full]] ? "/" : " "}] +assert_complete "~$full$trail" "ls ~$part" $test + + +sync_after_int + + teardown diff --git a/test/unit/_tilde.exp b/test/unit/_tilde.exp new file mode 100644 index 0000000..54394cb --- /dev/null +++ b/test/unit/_tilde.exp @@ -0,0 +1,51 @@ +# @param string $part Reference to variable to hold partial unique username +# @param string $full Reference to variable to hold full unique username +proc setup {part full} { + upvar $part _part + upvar $full _full + + assert_bash_exec {compgen -u} {} /@ users + find_unique_completion_pair $users _part _full + save_env +} + + +proc teardown {} { + assert_env_unmodified { + /COMPREPLY=/d + } +} + + +setup part full + + +set test "function should run without errors" +assert_bash_exec {_tilde > /dev/null} $test + + +sync_after_int + + +set test "function should not pollute environment" +# NOTE: A possible environment pollution is detected by assert_env_modified() in teardown() +assert_bash_exec {foo() { local aa="~"; _tilde "$aa"; }; foo; unset foo} $test + + +sync_after_int + + +set test "~full should complete to ~full unmodified" +set cmd [format {_tilde "~%s"; printf "%%s" "${COMPREPLY[@]}"} $full] +assert_bash_list "~$full" $cmd $test + + +sync_after_int + + +set test "~part should complete to ~full" +set cmd [format {_tilde "~%s"; printf "%%s" "${COMPREPLY[@]}"} $part] +assert_bash_list "~$full" $cmd $test + + +teardown