Advertisement
skozombie

Bash completion script for docta

May 6th, 2025
238
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 7.92 KB | None | 0 0
  1. #!/bin/bash
  2. # Autocomplete script for docta bash completion. Stand alone and generated at runtime
  3.  
  4. _docta_completion() {
  5.     # Function to check if a string is in an array
  6.     string_in_array() {
  7.         local target="$1"
  8.         shift
  9.         local array=("$@")
  10.  
  11.         for element in "${array[@]}"; do
  12.             if [[ "$element" == "$target" ]]; then
  13.                 return 0  # Return success (found)
  14.             fi
  15.         done
  16.  
  17.         return 1  # Return failure (not found)
  18.     }
  19.  
  20.     debug() {
  21.         [[ -n $COMP_DEBUG ]] && echo "$*" >> /tmp/completion_debug.log
  22.     }
  23.  
  24.     debug "## completion called for '${COMP_WORDS[@]}' ##"
  25.  
  26.     # Set local to avoid namespace pollution
  27.     local cur_word prev_word prev_arg prev_args all_args available multi mapped_arg solos
  28.     local need_file need_dir need_str need_template need_file_or_dir_or_str
  29.     local files ext need_file_arg need_dir_arg
  30.  
  31.     local -A arg_map=(
  32.         ["-h"]="--help"        
  33.         ["--help"]="-h"        
  34.         ["-v"]="--version"        
  35.         ["--version"]="-v"        
  36.         ["-q"]="--quiet"        
  37.         ["--quiet"]="-q"        
  38.         ["-d"]="--debug"        
  39.         ["--debug"]="-d"
  40.     )
  41.  
  42.     local -A ext_map=(
  43.         ["--pdf"]="pdf"
  44.         ["--html"]="html"
  45.     )
  46.  
  47.     # All the arguments generally available
  48.     all_args=('-h' '--help' '-v' '--version' '--install' '--bash-completion' '--init' '--check' '-q' '--quiet' '-d' '--debug' '--pdf' '--html')
  49.     # Arguments that can be specified multiple times
  50.     multi=()
  51.     # Arguments that should only be by themselves with no other arguments
  52.     solos=('-h' '--help' '-v' '--version' '--install' '--bash-completion' '--init' '--check')
  53.     # Arguments that need files
  54.     need_file=('--pdf' '--html')
  55.     # Arguments that need directories
  56.     need_dir=()
  57.     # Arguments that need a string argument that can't be completed
  58.     need_str=()
  59.     # Arguments that need a valid template
  60.     need_template=()
  61.  
  62.     # Current word we're trying to complete
  63.     cur_word="${COMP_WORDS[COMP_CWORD]}"
  64.     # Previous word we completed, needed for identifing options that need files/ strings/ etc
  65.     prev_word="${COMP_WORDS[COMP_CWORD - 1]}"
  66.     # Arguments excluding the one we're currently working on and the name of the program
  67.     if (( COMP_CWORD > 1 )); then
  68.         prev_args=("${COMP_WORDS[@]:1:COMP_CWORD-1}")
  69.     else
  70.         prev_args=()
  71.     fi
  72.  
  73.     # Do we still need a file argument (or can we take one at least)
  74.     need_file_arg=0
  75.     # Can we take a directory arg
  76.     need_dir_arg=1
  77.     # Can we take many positional arguments
  78.     pos_arg_count=1    
  79.  
  80.     # Arguments available for completion, to be calculated
  81.     available=()
  82.  
  83.     # Sort arguments to make things prettier
  84.     all_args=($(printf "%s\n" "${all_args[@]}" | sort))
  85.  
  86.     # Block further completion if the argument should be only by itself
  87.     for arg in "${prev_args[@]}"; do
  88.         mapped_arg="${arg_map[$arg]}"
  89.         if string_in_array "${arg}" "${solos[@]}" || string_in_array "${mapped_arg}" "${solos[@]}"; then
  90.             # BAIL! we've go args that should be used alone
  91.             debug "Found solo option ${arg} so aborting completion"
  92.             return
  93.         fi
  94.     done
  95.  
  96.     prev_arg=""
  97.  
  98.     need_file_or_dir_or_str=("${need_file}" "${need_dir}" "${need_str}")
  99.  
  100.     for arg in "${prev_args[@]}"; do
  101.         # If we're looking for a positional argument and this looks like it might be a file/ dir
  102.         if (( need_dir_arg || need_file_arg )) && (( pos_arg_count > 0 )) && [[ ${arg} != -* ]]; then
  103.             # If the previous arg wasn't something that needed a file/dir/str
  104.             if ! string_in_array "${prev_arg}" "${need_file_or_dir_or_str}"; then
  105.                 debug "Don't need a dir or file as we have ${arg}"
  106.                 # Cancel out looking for files and dirs
  107.                 need_dir_arg=0
  108.                 need_file_arg=0
  109.             fi
  110.         fi
  111.         prev_arg="${arg}"
  112.     done
  113.  
  114.     # Remove existing args that are not able to be used multiple times or that are solo if we already have args
  115.     for arg in "${all_args[@]}"; do
  116.         mapped_arg="${arg_map[$arg]}"
  117.         if string_in_array "${arg}" "${multi[@]}" || string_in_array "${mapped_arg}" "${multi[@]}"; then
  118.             debug "${arg} (or ${mapped_arg}) is in multi so can use again"
  119.             available=("${available[@]}" "${arg}")
  120.         elif ! string_in_array "${arg}" "${prev_args[@]}" && ! string_in_array "${mapped_arg}" "${prev_args[@]}"; then
  121.             available=("${available[@]}" "${arg}")
  122.         fi
  123.     done
  124.  
  125.     if string_in_array "${prev_word}" "${need_str[@]}"; then
  126.         debug "We need a string for ${prev_word} so not offering completion"
  127.         return        # Do nothing if we want a string
  128.     elif string_in_array "${prev_word}" "${need_dir[@]}"; then
  129.         # If we have a directory ending in a slash for the current completion add it so we can get subdirectories too
  130.         if [[ -d "${cur_word}" && "${cur_word}" =~ /$ ]]; then
  131.             COMPREPLY=("${cur_word}" $(compgen -o dirnames -- "${cur_word}" | sed 's/[^ ]* */&\//g'))
  132.         else
  133.             # Just get directories as per normal but add a slash so they're clearly dirs
  134.             COMPREPLY=($(compgen -o dirnames -- "${cur_word}" | sed 's/[^ ]* */&\//g'))
  135.  
  136.             # Don't just allow the completion of a directory in case there are sub directories, more tabs will show or
  137.             # complete sub directories
  138.             compopt -o nospace
  139.         fi
  140.     elif string_in_array "${prev_word}" "${need_file[@]}" && [[ "${cur_word}" != -* ]]; then
  141.         # Our previous arugment needs a file and our current argument doesn't look like a parameter
  142.         ext="${ext_map[${prev_word}]}"
  143.         if [[ -n $ext ]]; then
  144.             debug "Argument ${prev_word} needs a file so offering ${ext} files"
  145.             files=$(compgen -f -- "${cur_word}" | grep -E "\.${ext}$")
  146.         else
  147.             debug "Argument ${prev_word} needs a file but don't know the extention'"
  148.             files=$(compgen -f -- "${cur_word}" )
  149.         fi
  150.  
  151.         COMPREPLY=($(compgen -o dirnames -- "${cur_word}" | sed 's/[^ ]* */&\//g') "${files}")
  152.         # If we don't have a file (eg, only a directory) that's not a valid completion so don't add a space
  153.         if [[ ! -f "${cur_word}" ]]; then
  154.             compopt -o nospace
  155.         fi
  156.     elif string_in_array "${prev_word}" "${need_template[@]}"; then
  157.         # Provide templates as a word list but don't treat them as files
  158.         templates=($(docta -l | grep "^ " | tr -d " "))
  159.         COMPREPLY=($(compgen -W "$(IFS=$'\n'; echo "${templates[*]}")" -- "${cur_word}"))
  160.     elif [[ -d "${cur_word}"  && ! "${cur_word}" =~ /$ ]]; then
  161.         # If we have a directory and it doesn't end with a slash, update it to have a slash and use that
  162.         cur_word="${cur_word}/"
  163.         COMP_WORDS[COMP_CWORD]="${cur_word}"
  164.         COMPREPLY=($(compgen -f -- "${cur_word}" | grep -E '\.sql$'))
  165.         compopt -o nospace
  166.     elif [[ "${cur_word}" == -* ]]; then
  167.         # if we're completing an option, don't check for files
  168.         COMPREPLY=($(compgen -W "$(IFS=$'\n'; echo "${available[*]}")" -- "${cur_word}"))
  169.     elif [[ "${need_file_arg}" != 0 && -n "${cur_word}" ]]; then
  170.         # If we still need a file, build the options
  171.         COMPREPLY=($(compgen -o dirnames -- "${cur_word}" | sed 's/[^ ]* */&\//g') $(compgen -f -- "${cur_word}" ))
  172.         # If we don't have a file (eg, only a directory) that's not a valid completion so don't add a space
  173.         if [[ ! -f "${cur_word}" ]]; then
  174.             compopt -o nospace
  175.         fi
  176.     else
  177.         if [[ "${need_dir_arg}" -ne 0 ]]; then
  178.             local dirs=($(compgen -o dirnames -- "${cur_word}" | sed 's/[^ ]* */&\//g'))
  179.         fi
  180.  
  181.         COMPREPLY=($(compgen -W "$(IFS=$'\n'; echo "${available[*]}")" -- "${cur_word}") "${dirs[@]}" "${files[@]}")
  182.     fi
  183. }
  184.  
  185. complete -o nosort -F _docta_completion docta
  186.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement