## # Bash completion adapter for `locd` # # `source` me! # # @note # I had through about trying to use $BASHPID to perhaps keep state on a # completion server of some type, but it changes with every invocation... # would need to reach the parent process' PID somehow, assuming that's even # how completion works. # # @note # It looks like `complete` (the Bash builtin that facilitates completion) # can take a command to execute instead of a function. # # Since we're going to have to shell-out in some way, that might be better # so we can write the logic in Ruby (or at least not bash). Assuming it # sets the completion ENV vars and you work from there, so we would lose # the `_init_completion` functionality from `bash-completion` (v2). # ## # Functions # ============================================================================ # Append a message to $LOCD_BASH_COMP_DEBUG_FILE if the var is not empty. # __<%= name %>_debug() { if [[ -n ${LOCD_BASH_COMP_DEBUG_FILE} ]]; then echo "$*" >> "${LOCD_BASH_COMP_DEBUG_FILE}" fi } # Entry point for bash completion. # __<%= name %>_complete() { # Timing is commented out 'cause depends on a ms-resolution bin I built # at some point and don't want to figure that out... # local start_time="$(ms)" __<%= name %>_debug "${FUNCNAME[0]}: STARTing the show...." local cur prev words cword split _init_completion -s || return __<%= name %>_debug "${FUNCNAME[0]}: initialized" \ "cur='$cur'" \ "prev='$prev'" \ "words='${words[@]}'" \ "cword='$cword'" \ "split='${split}'" __<%= name %>_debug "${FUNCNAME[0]}: Getting respond from app bin at ${words[0]}" local response="$(${words[0]} bash-complete complete -- "$cur" "$prev" "${cword}" "${split}" "${words[@]}")" # We need to figure out if we're about to complete a `--flag=` word, in # which case we want to turn `nospace` on, do the compelte, then # turn it back off so we don't add an annoying space after the `=`. # # We'll need these variables for the logic: # local nospace local response_array=( $response ) local response_array_length="${#response_array[@]}" local response_length="${#response}" __<%= name %>_debug "${FUNCNAME[0]}: Got response;" \ "response='$response'" \ "response_length='${response_length}'" \ "response_array='${response[@]}'" \ "response_array_length='${response_array_length}'" # We're going to flip `nospace` off if ALL of: # # 1. There is only one "word" element in the response. # # 2. The response length (in characters) is greater than zero # (this might be redundant, was here before (1)). # # 3. The last character of the single word is `=`. # # If all of those are true, then `nospace` var should be set to "true". # # This probably isn't the most bullet-proof logic, but it's a start... # if (( response_array_length == 1 && response_length > 0 )); then local index=$response_length ((index--)) local last_char="${response:$index:1}" [ $last_char == "=" ] && nospace=true fi # If we indeed set `nospace` then flip `nospace` off if [[ -n "$nospace" ]]; then compopt +o nospace fi COMPREPLY=( $(compgen -W "$response" -- "$cur" ) ) # If we had turned `nospace` off, turn it back on if [[ -n "$nospace" ]]; then compopt -o nospace unset nospace fi # See comment up top about `ms` bin... # local end_time="$(ms)" # local delta_s="$((end_time-start_time))" # __<%= name %>_debug "${FUNCNAME[0]}: We're DONE here ($delta_s ms)." __<%= name %>_debug "${FUNCNAME[0]}: We're DONE here." __<%= name %>_debug return 0 } # Binding # ============================================================================ # Hook in the `__locd_debug` function to `complete` # # @see https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html # if [[ $(type -t compopt) = "builtin" ]]; then # -o default (option `default`) # Use Readline’s default filename completion if the compspec generates # no matches. # # -F __locd_complete # Tell `complete` to execute the `__locd_complete` function in the current # shell environemnt. # complete -o default -F __<%= name %>_complete <%= bin %> # complete -o default -C 'locd complete-cmd' locd else # UNTESTED! `compopt` is builtin for me. # # -o default (option `default`) # Use Readline’s default filename completion if the compspec generates # no matches. # # -o nospace (option `nospace`) # "Tell Readline not to append a space (the default) to words completed at # the end of the line." (via docs, see above). # # Not sure why this is here, guessing if `compopt` is *not* a `builtin` # then maybe that detects and older version? My `bash@4.4.23` has # `compopt` as a `builtin`, so have not taken this branch. # # -F __locd_complete # Tell `complete` to execute the `__locd_complete` function in the current # shell environemnt. # complete -o default -o nospace -F __<%= name %>_complete <%= bin %> fi