Advertisement
skozombie

Example script for complicated context sensitive bash completion

Dec 8th, 2023
892
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 6.28 KB | Source Code | 0 0
  1. #!/bin/bash
  2. # Autocomplete script for dbzeug bash completion. Stand alone and generated at runtime
  3.  
  4. # Function to check if a string is in an array
  5. string_in_array() {
  6.     local target="$1"
  7.     shift
  8.     local array=("$@")
  9.  
  10.     for element in "${array[@]}"; do
  11.         if [[ "$element" == "$target" ]]; then
  12.             return 0  # Return success (found)
  13.         fi
  14.     done
  15.  
  16.     return 1  # Return failure (not found)
  17. }
  18.  
  19. _dbzeug_completion() {
  20.     local cur_word prev_word prev_args all_args available multi mapped_arg solos need_dir need_str need_template need_file
  21.  
  22.     local -A arg_map=(
  23.         ["-h"]="--help"        
  24.         ["--help"]="-h"        
  25.         ["-l"]="--list"        
  26.         ["--list"]="-l"        
  27.         ["-o"]="--output"        
  28.         ["--output"]="-o"        
  29.         ["-t"]="--template"        
  30.         ["--template"]="-t"        
  31.         ["-c"]="--console"        
  32.         ["--console"]="-c"        
  33.         ["-i"]="--include"        
  34.         ["--include"]="-i"        
  35.         ["-x"]="--exclude"        
  36.         ["--exclude"]="-x"        
  37.         ["-j"]="--json"        
  38.         ["--json"]="-j"        
  39.         ["-d"]="--debug"        
  40.         ["--debug"]="-d"
  41.     )
  42.  
  43.     # All the arguments generally available
  44.     all_args=('-h' '--help' '-l' '--list' '-o' '--output' '-t' '--template' '-c' '--console' '--drops' '-i' '--include' '-x' '--exclude' '--integrate' '-j' '--json' '-d' '--debug' '--version' '--validate' '--fix' '--template-dir' '--user-field' '--verbose' '--install' '--bash-completion')
  45.     # Arguments that can be specified multiple times
  46.     multi=('-t' '--template')
  47.     # Arguments that should only be by themselves with no other arguments
  48.     solos=('-h' '--help' '-l' '--list' '--version' '--install' '--bash-completion')
  49.     # Arguments that need directories
  50.     need_dir=('-o' '--output' '--template-dir')
  51.     # Arguments that need a string argument that can't be completed
  52.     need_str=('-i' '--include' '-x' '--exclude' '--user-field')
  53.     # Arguments that need a valid template
  54.     need_template=('-t' '--template')
  55.        
  56.     # Current word we're trying to complete
  57.     cur_word="${COMP_WORDS[COMP_CWORD]}"
  58.     # Previous word we completed, needed for identifing options that need files/ strings/ etc
  59.     prev_word="${COMP_WORDS[COMP_CWORD - 1]}"
  60.     # Arguments excluding the one we're currently working on
  61.     prev_args=("${COMP_WORDS[@]::${#COMP_WORDS[@]}-1}")
  62.    
  63.     # Do we still need a file argument (or can we take one at least)
  64.     need_file=1
  65.     # Arguments available for completion, to be calculated
  66.     available=()
  67.    
  68.     # Sort arguments to make things prettier
  69.     all_args=($(printf "%s\n" "${all_args[@]}" | sort))
  70.  
  71.     # Block further completion if the argument should be only by itself
  72.     for arg in "${prev_args[@]}"; do
  73.         mapped_arg="${arg_map[$arg]}"
  74.         if string_in_array "${arg}" "${solos[@]}" || string_in_array "${mapped_arg}" "${solos[@]}"; then
  75.             # BAIL! we've go args that should be used alone
  76.             return
  77.         fi
  78.     done
  79.  
  80.     # Check if we still need a file
  81.     for arg in "${prev_args[@]}"; do
  82.         if [[ "${arg}" == *.sql ]]; then
  83.             need_file=0
  84.             break
  85.         fi
  86.     done
  87.  
  88.     # Remove existing args that are not able to be used multiple times
  89.     for arg in "${all_args[@]}"; do
  90.         mapped_arg="${arg_map[$arg]}"
  91.         if string_in_array "${arg}" "${multi[@]}" || string_in_array "${mapped_arg}" "${multi[@]}"; then
  92.             available=("${available[@]}" "${arg}")
  93.         elif ! string_in_array "${arg}" "${prev_args[@]}" && ! string_in_array "${mapped_arg}" "${prev_args[@]}"; then
  94.             available=("${available[@]}" "${arg}")
  95.         fi
  96.     done
  97.  
  98.     if string_in_array "${prev_word}" "${need_str[@]}"; then
  99.         return        # Do nothing if we want a string
  100.     elif string_in_array "${prev_word}" "${need_dir[@]}"; then
  101.         # If we have a directory ending in a slash for the current completion add it so we can get subdirectories too
  102.         if [[ -d "${cur_word}" && "${cur_word}" =~ /$ ]]; then
  103.             COMPREPLY=("${cur_word}" $(compgen -o dirnames -- "${cur_word}" | sed 's/[^ ]* */&\//g'))
  104.         else
  105.             # Just get directories as per normal but add a slash so they're clearly dirs
  106.             COMPREPLY=($(compgen -o dirnames -- "${cur_word}" | sed 's/[^ ]* */&\//g'))
  107.  
  108.             # Don't just allow the completion of a directory in case there are sub directories, more tabs will show or
  109.             # complete sub directories
  110.             compopt -o nospace
  111.         fi
  112.     elif string_in_array "${prev_word}" "${need_template[@]}"; then
  113.         # Provide templates as a word list but don't treat them as files
  114.         templates=($(dbzeug -l | grep "^ " | tr -d " "))
  115.         COMPREPLY=($(compgen -W "$(IFS=$'\n'; echo "${templates[*]}")" -- "${cur_word}"))
  116.     elif [[ -d "${cur_word}"  && ! "${cur_word}" =~ /$ ]]; then
  117.         # If we have a directory and it doesn't end with a slash, update it to have a slash and use that
  118.         cur_word="${cur_word}/"
  119.         COMP_WORDS[COMP_CWORD]="${cur_word}"
  120.         COMPREPLY=($(compgen -f -- "${cur_word}" | grep -E '\.sql$'))
  121.         compopt -o nospace
  122.     elif [[ "${cur_word}" == -* ]]; then
  123.         # if we're completing an option, don't check for files
  124.         COMPREPLY=($(compgen -W "$(IFS=$'\n'; echo "${available[*]}")" -- "${cur_word}"))
  125.     elif [[ "${need_file}" != 0 && -n "${cur_word}" ]]; then
  126.         # If we still need a file, build the options
  127.         COMPREPLY=($(compgen -o dirnames -- "${cur_word}" | sed 's/[^ ]* */&\//g') $(compgen -f -- "${cur_word}" | grep -E '\.sql$'))
  128.         # If we don't have a file (eg, only a directory) that's not a valid completion so don't add a space
  129.         if [[ ! -f "${cur_word}" ]]; then
  130.             compopt -o nospace
  131.         fi
  132.     else
  133.         # If no specific option is provided, complete with both arguments and files
  134.         if [[ "${need_file}" != 0 ]]; then
  135.             local files=($(compgen -o dirnames -- "${cur_word}" | sed 's/[^ ]* */&\//g') $(compgen -f -- "${cur_word}" | grep -E '\.sql$'))
  136.         fi
  137.  
  138.         COMPREPLY=($(compgen -W "$(IFS=$'\n'; echo "${available[*]}")" -- "${cur_word}") "${files[@]}")
  139.     fi
  140. }
  141.  
  142. complete -o nosort -F _dbzeug_completion dbzeug
  143.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement