gnuastro-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnuastro-commits] master 4598eba 54/62: Auto-completion: clean re-imple


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master 4598eba 54/62: Auto-completion: clean re-implementation for Table
Date: Thu, 13 May 2021 22:20:54 -0400 (EDT)

branch: master
commit 4598eba89d2c66df7d28c20838e2c1190777dbe7
Author: Mohammad Akhlaghi <mohammad@akhlaghi.org>
Commit: Mohammad Akhlaghi <mohammad@akhlaghi.org>

    Auto-completion: clean re-implementation for Table
    
    Until now, Pedram had made great progress with creating a beautiful core
    infrastructure and developer documentation for the autocompletion. It was
    so well written that I was easily able to experiment and go through the
    model and understand its inner workings. In order to generalize it for any
    generic Gnuastro program and make it scalable to many options, I noticed
    that it is easier to do a clean re-implementation of the code.
    
    With this commit, the first full re-implementation of the completion code
    has been written for Table following the modularity and scalability
    principles (to easily extend to other programs). It is now clearly
    separated into parts that should be general for all programs and parts that
    are custom built for each individual program.
---
 bin/table/completion.bash | 1088 +++++++++++++++++++++++++++++----------------
 1 file changed, 717 insertions(+), 371 deletions(-)

diff --git a/bin/table/completion.bash b/bin/table/completion.bash
index 3298488..b0d3574 100644
--- a/bin/table/completion.bash
+++ b/bin/table/completion.bash
@@ -1,9 +1,12 @@
-#!/bin/sh
+#!/bin/bash
 
-# Add bash autocompletion to Gnuastro. This shell script is intended to
-# load itself automatically from the '~/.bashrc' file, modified during
+# Bash autocompletion to Gnuastro. This shell script is intended to load
+# itself automatically from the '~/.bashrc' file, modified during
 # installation. For more details, see the 'autocomplete feature' under the
-# 'developing' section of Gnuastro's manual and the comments below.
+# 'developing' chapter of Gnuastro's manual and the comments below.
+#
+# To debug/test this script, you can simply 'source' it into your running
+# terminal.
 #
 # Original author:
 #     Pedram Ashofteh Ardakani <pedramardakani@pm.me>
@@ -27,528 +30,871 @@
 
 
 
+#######################################################################
+############      Options and general operating mode       ############
+#######################################################################
 
+# GLOBAL VARIABLES
+_gnuastro_prefix="/usr/local/bin";
 
 
-# In the developing phase, call the command below to try autocompletion in
-# action:
-#
-# $ source completion.sh
 
 
+# Basic initialization.
+_gnuastro_autocomplete_initialize(){
 
+    # Initialize the completion response with null
+    COMPREPLY=();
+
+    # Variable "current", is the current word being completed. "$2" is the
+    # default value for the current word in completion scripts. But we are
+    # using the longer form: "${COMP_WORDS[COMP_CWORD]}" for clarity.
+    current="${COMP_WORDS[COMP_CWORD]}"
+    if [ "$current" = "=" ]; then
 
+        # The equal sign '=' raises complexities when filling suggestions
+        # for long options. Things will work out fine when they are simply
+        # ignored.
+        current=""
 
+    fi
 
-# GLOBAL VARIABLES
-<<<<<<< HEAD
-db=1
-PREFIX="/usr/local/bin";
-ASTFITS="$PREFIX/astfits";
-ASTTABLE="$PREFIX/asttable";
-db=0 # Set 0 for printing debug messages, else set to 1
-=======
-_gnuastro_completion_debug=1
-_gnuastro_prefix="/usr/local/bin";
-_gnuastro_astfits="$_gnuastro_prefix/astfits";
-_gnuastro_asttable="$_gnuastro_prefix/asttable";
->>>>>>> e18d46e4 (bin/table/completion.bash: improvements to find good table 
name)
+    # Variable "prev", is one word before the one being completed. By
+    # default, this is set as "$3" in completion scripts. But we are using
+    # the longer form: "${COMP_WORDS[COMP_CWORD-1]}" to avoid confusions
+    # with the arguments of our internal functions. Note that Bash will
+    # return the '=' sign as a separate component of the line, so in that
+    # case, we want the second-last word.
+    prev="${COMP_WORDS[COMP_CWORD-1]}"
+    if [ "$prev" = "=" ]; then
+
+        # While a user is writing a long option's argument, the previous
+        # word will be the equal sign '='. This is not helpful at all. But
+        # looking at a word just before '=' helps us understand which long
+        # option is being called upon.
+        prev="${COMP_WORDS[COMP_CWORD-2]}"
+
+    fi
+}
+
+
+
+
+
+# List all the options of the given program (an '=' is kept for the options
+# that need a value). In the output of '--help', option lines have these
+# properties:
+#
+#   - The full line starts with two empty spaces.
+#   - The first non-white character is a '-'.
+#   - It contains a long option formated like '--XXXX'.
+#
+# But some options have a short version (which we ignore in
+# autocompletion), so if the first word ends with a ',' the option name we
+# want to show is the second word.
+_gnuastro_autocomplete_option_list(){
+    options_all=$("${COMP_WORDS[0]}" --help \
+                      | awk '/^  / && $1 ~ /^-/ && /--+[a-zA-Z0-9]*/ { \
+                              if($1 ~ /,$/) name=$2; \
+                              else          name=$1; \
+                              print name}' \
+                      | sed -e's|=.*|=|');
+}
 
 
 
 
 
-# Accept either an array or a string '$1' split by normal bash conventions,
-# check if second argument '$2' is present in the suggestions, if so, put
-# it in suggestions, continue otherwise. Note, in case the second agument
-# is not passed, no filteration will be applied.
-_gnuastro_autocomplete_compgen(){
-    for w in $1
-    do
-        # The right-hand side of '[[ ... =~ ... ]]' is considered a regex
-        # and should not be quoted or it will be taken as a literal.
-        # TODO: Let 'sed' or 'awk' do the regex matching if it screws up
-        # the portability.
-        [[ "$w" =~ $2 ]] && COMPREPLY+=( "$w" )
+# Return successfully if the previous token (specified as first argument)
+# is an option that requires a value and store the option name if it is.
+#
+#   INPUT:
+#     1) [as argument] String to search for.
+#     *) [as variable] 'options_all' (in case the list of options is
+#                      already read)
+_gnuastro_autocomplete_string_is_valued_option(){
+
+    # For easy reading.
+    local string=$1
+
+    # If the first character of the string isn't a '-', then its not an
+    # option and there is no need to do any futher checks (and slow down
+    # the output) so we can safely return failure.
+    if [ ${string:0:1} != "-" ]; then return 1; fi
+
+    # List of option names (with an '=' after those that need a value.
+    if [ x"$options_all" = x ]; then
+        _gnuastro_autocomplete_option_list
+    fi
+
+    # Go over the option list and see if they match the '$1'.
+    for option in $options_all; do
+        if [[ $option = $string=* ]]; then return 0; fi
     done
+
+    # If control reaches here, then return failure (1).
+    return 1
 }
 
 
 
 
 
+# See if the current word is an argument or option value.
+_gnuastro_autocomplete_mode_arg_optval(){
 
-# Check if the given file is a FITS file (that can actually be
-# opened). Note that FITS files have many possible extensions (see the
-# 'gal_fits_name_is_fits' function in 'lib/fits.c').
-_gnuastro_autocomplete_is_fits(){
-    local inputfile="$1"
-    if $_gnuastro_astfits $inputfile -h0 &> /dev/null; then return 0; else 
return 1; fi
+    # If the previous token is the first token, then this is an
+    # argument, no need for any further checks.
+    if [ $prev = "${COMP_WORDS[0]}" ]; then
+        argument=$current
+
+    # If the previous token is an option that needs a value, then this is
+    # an option value, this function will set 'option_name' and
+    # 'option_name_complete' if necessary.
+    elif _gnuastro_autocomplete_string_is_valued_option $prev; then
+        option_name=$prev
+        option_value=$current
+        option_name_complete=1
+
+    # The previous token wasn't an option that required a value, so this is
+    # an argument.
+    else
+        argument=$current
+    fi
 }
 
 
 
 
 
-# 'Append' all 'FITS' files in current directory to suggestions. Case
-# insensitive.  The -X option and its filter pattern are explained on bash
-# programmable completion info page: $ info bash programmable 'Appending'
-# seems a good idea because the program might accept multiple input types.
-#
-# For example the 'asttable' program can either accept a fits file or
-# various short/long options as its first argument. In this case,
-# autocompletion suggests both.
-#
-# The completion can not suggest filenames that contain white space in them
-# for the time being.
+# See if this is an argument, option name or option value. This function
+# will fill 'argument', 'option_name' and 'option_value' (they should be
+# initialized to an empty string before it).
 #
-# List all files in the current directory. Filter out those that start
-# with the current word in the command line '$word'. Note that the grep
-# option '--color=never' has to be there to prevent passing special
-# characters into '$COMPREPLY'.
-_gnuastro_autocomplete_list_fits_names(){
-    local files=($(ls | grep -e "^$word" --color=never))
-    for f in ${files[*]} ; do
-        if _gnuastro_autocomplete_is_fits "$f"; then COMPREPLY+=("$f"); fi
-    done
+#  option_value=FULL:            We are busy completing an option value.
+#  option_name=FULL:             Can mean different meanings.
+#    {
+#      option_name_complete==0:  We are still filling the option name.
+#      option_name_complete==1:  We are starting the option values.
+#    }
+#  argument=FULL:                We are busy filling an argument.
+#  argument=EMPTY:               We haven't started writing an argument yet.
+_gnuastro_autocomplete_mode(){
+
+    # Local variable necessary only in this function.
+    local namevalue=""
+
+    # If the current word is empty, it may be an argument, or value of an
+    # option (in case a value-required option is given before).
+    if [ x$current = x ]; then
+        _gnuastro_autocomplete_mode_arg_optval
+
+    # The current word isn't empty.
+    else
+
+        # If the current word starts with a '-', it is an option name or
+        # 'name=value' pair.
+        if [ ${current:0:1} = "-" ]; then
+
+            # If there is an equal sign, then we should separate the option
+            # name from the value and keep
+            if [[ $current = *=* ]]; then
+
+                # By setting the "internal field separator" (IFS) to '='
+                # and using 'read', we can separate the strings before and
+                # after the equal sign.
+                IFS="=" read -ra namevalue <<< $current
+                option_name=${namevalue[0]}
+                option_name_complete=1
+
+                # If the value isn't written yet, (for example '--hdu='),
+                # then the second string will just be an '='. But no value
+                # is given yet, so 'option_value' should be empty.
+                option_value=${namevalue[1]}
+                if [ x$option_value = x"\=" ]; then option_value=""; fi
+            else
+                option_name=$current
+                option_name_complete=0
+            fi
+
+        # The current word didn't start with a '-', so it may be an
+        # argument or option value.
+        else
+            # Bash may separate the '=' in 'name=value' tokens. In this
+            # scenario, when the user only gives 'name=' and presses TAB,
+            # then 'current' will be '='. In this case, we should just set
+            # it to empty.
+            if [ $current = "=" ]; then current=""; fi
+
+            # Check to see if its an argument or option value.
+            _gnuastro_autocomplete_mode_arg_optval
+        fi
+
+    fi
 }
 
 
 
 
-_gnuastro_autocomplete_list_plaintext_tables(){
-    local files=($(ls | grep -e "^$word" --color=never))
-    for f in ${files[*]} ; do
-        if _gnuastro_autocomplete_is_plaintext_table "$f"; then
-            COMPREPLY+=("$f"); fi
+# Given a certain option (as first argument), find the value that the user
+# has given for it.
+#
+# OUTPUT:
+#   read_option_value
+_gnuastro_autocomplete_read_option_value(){
+
+    # Inputs:
+    local read_option_name=$1
+
+    # Initialize the output (defined as 'local' before this).
+    read_option_value=""
+
+    # Parse through the given command-line and find the value.
+    local option_found=0
+    for word in ${COMP_WORDS[*]}; do
+
+        # Ignore the program name (first word), current (last) word and any
+        # '=' signs.
+        if [ x$word = x${COMP_WORDS[0]} ] \
+               || [ x$word = x$current ] \
+               || [ x$word = x"=" ]; then
+            local just_a_place_holder=1
+        else
+            # If the 'option_found' flag is set, this is the answer, set it
+            # and return (this has to be *before* the place that we set
+            # 'option_found').
+            if [ $option_found = 1 ]; then
+                read_option_value="$word";
+                return 0
+            fi
+
+            # If this word is the desired option, set the 'option_found'
+            # flag so the next word is taken as the value.
+            if [ x$word = x$read_option_name ]; then option_found=1; fi
+        fi
     done
 }
 
 
 
 
-# List all files that are considered as valid input. This is here to
-# prevent going throught a list of files serveral times for each extension.
-_gnuastro_autocomplete_list_all_valid_files(){
-    local files=($(ls | grep -e "^$word" --color=never))
-    for f in ${files[*]} ; do
-        if _gnuastro_autocomplete_is_plaintext_table "$f"; then
-            COMPREPLY+=("$f"); fi
-        if _gnuastro_autocomplete_is_fits "$f"; then COMPREPLY+=("$f"); fi
-    done
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#######################################################################
+############                     Files                     ############
+#######################################################################
+
+# Check if the given file is a FITS file (that can actually be
+# opened). Note that FITS files have many possible extensions (see the
+# 'gal_fits_name_is_fits' function in 'lib/fits.c').
+_gnuastro_autocomplete_is_fits(){
+    if "$_gnuastro_prefix"/astfits "$1" -h0 &> /dev/null; then return 0;
+    else                                                       return 1;
+    fi
 }
 
 
 
 
-# Prompt the user that this option only accepts a number
-_gnuastro_autocomplete_expect_number(){
-    echo "Pass"
+
+# Return successfully if argument (a FITS file) has a image HDU.
+_gnuastro_autocomplete_fits_has_image(){
+    if _gnuastro_autocomplete_is_fits "$1"; then
+        if [ $("$_gnuastro_prefix"/astfits "$1" --hasimagehdu) = 1 ]; then
+            return 0
+        fi
+    fi
+    return 1
 }
 
 
 
-# Accepts a FITS filename as input and echoes its headers.
-_gnuastro_autocomplete_get_fits_hdu(){
-    local inputfile="$1"
-    $_gnuastro_astfits --quiet $inputfile | awk '{print $2}'
+
+
+# Return successfully if argument (a FITS file) has a table HDU.
+_gnuastro_autocomplete_fits_has_table(){
+    if _gnuastro_autocomplete_is_fits "$1"; then
+        if [ $("$_gnuastro_prefix"/astfits "$1" --hastablehdu) = 1 ]; then
+            return 0
+        fi
+    fi
+    return 1
 }
 
 
 
 
 
-# Checks for the current fits file and puts its headers into completion
-# suggestions
-_gnuastro_autocomplete_list_fits_hdu(){
-    local inputfile="$1"
-    local list=("$(_gnuastro_autocomplete_get_fits_hdu $inputfile)")
-    # A custom enhancement for the 'compgen' command. This version will
-    # have no problem with the dash sign '-'. Because sometimes the
-    # 'hdu' names might contain dash symbols in them. This ensures that
-    # all of them are suggested.
-    _gnuastro_autocomplete_compgen "${list[@]}" "$word"
+# Return successfully if first argument is plain-text (not binary).
+_gnuastro_autocomplete_is_plaintext(){
+    if file "$1" | grep 'executable\|binary' &> /dev/null; then return 1;
+    else                                                        return 0;
+    fi
 }
 
 
 
 
 
-# This function is given the long and short formats of an option (as first
-# and second arguments). If the option has been called until now, and has
-# been given a value, its value will be returned. Note that if it is called
-# multiple times, only the last occurance is used. It should be used like
-# this:
-#
-#    _gnuastro_autocomplete_option_value --hdu -h
-_gnuastro_autocomplete_option_value(){
-    option_value=""
-    local longname=$1
-    local shortname=$2
-    for i in $(seq 1 ${#COMP_WORDS[@]}); do
-
-        # To keep things readable, put this token into a variable.
-        local token=${COMP_WORDS[i]}
-
-        # First, the easy long-format scenario.
-        if [ x$token = x$longname ]; then
-            if [ x${COMP_WORDS[i+1]} = x"=" ]; then
-                option_value="${COMP_WORDS[i+2]}"
+# Return successfully (with 0) if the given non-FITS file is a table.
+_gnuastro_autocomplete_is_plaintext_table(){
+
+    # Only do the check if the file exists.
+    if [ -f $1 ]; then
+
+        # If the file is not plain-text, it will contain an 'executable' or
+        # 'binary' in the output of the 'file' command.
+        if _gnuastro_autocomplete_is_plaintext "$1"; then
+
+            # The file is plain-text. Extract the first non-commented or
+            # empty line and feed it to 'asttable' to see if it can be
+            # interpretted properly. We don't want to bother with the other
+            # lines, because we don't want to waste computational power
+            # here.
+            if awk '!/^#/ && NF>0 {print; exit 0}' "$1" \
+                    | "$_gnuastro_prefix"/asttable &> /dev/null; then
+                return 0
             else
-                option_value="${COMP_WORDS[i+1]}"
+                return 1
             fi
 
-        # The short format. In the short format, the option is defined by
-        # its first two characters (the first is a '-' and the second can
-        # be any ASCII character. We don't have any '=' sign, however, the
-        # value may be touching the option (into one token), for example
-        # '-c1' and '-c 1' are the same.
-        #
-        # TODO: We still have to account for cases where there are short
-        # options with no values in the middle. For example assume that we
-        # also have option '-a' that is just an on-off option. In this
-        # case, according to the GNU Coding Standards, it is fine to say
-        # something like this: '-ac1'. This isn't too common, so in this
-        # first implementation, we are ignoring it due to lack of
-        # time. We'll add it later.
+            # The file was binary
+        else return 1
+        fi
+
+    # The file didn't exist.
+    else return 1
+    fi
+}
+
+
+
+
+
+# Return successfully if the first argument is a table.
+_gnuastro_autocomplete_is_table(){
+    if   _gnuastro_autocomplete_fits_has_table     $1; then return 0
+    elif _gnuastro_autocomplete_is_plaintext_table $1; then return 0
+    else                                                    return 1
+    fi
+}
+
+
+
+
+
+# If a table is already given in the previous tokens, return successfully
+# (with zero), and will put its name in 'given_file'. Otherwise, return a
+# failure (1) and 'given_file' will be untouched.
+_gnuastro_autocomplete_first_in_arguments(){
+
+    # Inputs
+    local mode=$1
+
+    # Local variables (that are only for this function).
+    local word=""
+    local previous=""
+
+    # Initialize outputs
+    given_file=""
+
+    # Go over all the words/tokens given until now.
+    for word in ${COMP_WORDS[*]}; do
+
+        # Ignore the program name (first word), current (last) word, any
+        # directories or '=', or any word that starts with a '-' (which is
+        # an option).
+        if [ x$word = x${COMP_WORDS[0]} ] \
+               || [ x$word = x$current ] \
+               || [ ${word:0:1} = "-" ] \
+               || [ x$word = x"=" ] \
+               || [ -d $word ]; then
+            local just_a_place_holder=1
         else
-            local first=${token:0:2}
-            if [ x"$first" = x"$shortname" ]; then
-                if [ x"$token" =  x"$first" ]; then
-                    option_value="${COMP_WORDS[i+1]}"
+            # If the previous word is a valued option, then it shouldn't be
+            # checked.
+            if _gnuastro_autocomplete_string_is_valued_option $previous; then
+                local just_a_place_holder=1
+            else
+
+                # Based on the mode, do the proper check.
+                if [ $mode = table ]; then
+                    if _gnuastro_autocomplete_is_table $word; then
+                        given_file=$word
+                        return 0;
+                    fi
                 else
-                    option_value=$(echo $token | sed 's|'${token:0:2}'||')
+                    if _gnuastro_autocomplete_fits_has_image $word; then
+                        given_file=$word
+                        return 0;
+                    fi
                 fi
             fi
         fi
-    done
-}
 
+        # If this word isn't an '=', put it in 'previous' and go onto the
+        # next word.
+        if [ $word != "=" ]; then
+            previous=$word
+        fi
+    done
 
+    # If control reached here, then there weren't any tables on the
+    # command-line until now.
+    return 1;
+}
 
 
 
-# Return 0 if the given non-FITS file is a table.
-_gnuastro_autocomplete_is_plaintext_table(){
 
-    # For easy reading.
-    local inputfile="$1"
 
-    # If the file is not plain-text, it will contain an 'executable' or
-    # 'binary' in the output of the 'file' command.
-    if file $inputfile \
-            | grep 'executable\|binary' &> /dev/null; then
-        return 1
-    else
-        # The file is plain-text. Extract the first non-commented or empty
-        # line and feed it to 'asttable' to see if it can be interpretted
-        # properly. We don't want to bother with the other lines, because
-        # we don't want to waste computational power here.
-        if awk '!/^#/ && NF>0 {print; exit 0}' $inputfile \
-                | $_gnuastro_asttable &> /dev/null; then
-            return 0
-        else
-            return 1
+# Find the requested table from the entered command-line.
+#
+# INPUT ARGUMENTS:
+#    1) Mode of file ('table' or 'image').
+#    2) Name of option containing file names.
+#
+# WRITTEN VARIABLES (should be defined before this function).
+#     given_file: file name of given table.
+_gnuastro_autocomplete_given_file(){
+
+    # Set inputs (for each readability).
+    local mode="$1"
+    local name="$2"
+
+    # If 'name' is emtpy, we should look in the arguments, otherwise, we
+    # should loook into the options.
+    if [ x"$name" = x ]; then
+        if _gnuastro_autocomplete_first_in_arguments $mode; then
+            # given_file is written by the function as a side-effect.
+            local just_a_place_holder=1
         fi
+    else
+        _gnuastro_autocomplete_read_option_value "$name"
+        given_file="$read_option_value"
     fi
+
 }
 
 
 
 
 
-# Reverse the list of existing strings on the command-line (so the last
-# word becomes the first), then check if it is an acceptable Table file in
-# there. This covers FITS and plaintext files so far.
-_gnuastro_autocomplete_last_table(){
+# Find the requested table within the already entered command-line. This
+# option needs two arguments:
+#
+# INPUT ARGUMENTS
+#    1) Mode of file ('table' or 'image').
+#    2) The filename option (if empty string, 1st argument).
+#    3) The HDU option (only necessary if the file is FITS).
+#
+# WRITTEN VARIABLES (should be defined before this function).
+#    given_file: file name of given table/image.
+#    given_hdu:  HDU of given table/table.
+_gnuastro_autocomplete_given_file_and_hdu(){
 
-    # Output variables.
-    last_table=""
-    last_table_hdu=""
-    last_table_hdu_auto=0
+    # Set inputs (for each readability).
+    local mode=$1
+    local name=$2
+    local hduoption=$3
 
     # Internal variables.
-    local token=""
-    local option_value=""
+    local read_option_value=""
+
+    # Initialize the outputs (defined before this).
+    given_hdu=""
+    given_file=""
+
+    # First, confirm the table file name. If requested table is in an
+    # argument, 'name' will be empty.
+    _gnuastro_autocomplete_given_file $mode $name
+
+    # If a file name existed and it is a FITS file, find the HDU given in
+    # the option.
+    if [ x"$given_file" != x ] \
+           && _gnuastro_autocomplete_is_fits "$given_file"; then
+        _gnuastro_autocomplete_read_option_value $hduoption
+        given_hdu="$read_option_value"
+    fi
+}
 
-    # Parse the tokens in reverse.
-    for token in $(echo "${COMP_WORDS[@]}" \
-                    | awk '{for(i=NF;i>1;--i)
-                          if ($i !~ /^-/) printf "%s ", $i}')
-    do
-        # First, make sure it is an existing file.
-        if [ -f $token ]; then
-
-            # It is a FITS file.
-            if _gnuastro_autocomplete_is_fits $token; then
-
-                # See if a HDU has been given or not. If it hasn't been
-                # given, its safe to assume the first HDU. In this case,
-                # we'll set the 'last_table_hdu_auto' variable to 1 (to
-                # help in debugging in later steps).
-                _gnuastro_autocomplete_option_value --hdu -h
-                if [ x"$option_value" = x ]; then
-                    last_table_hdu=1
-                    last_table_hdu_auto=1
-                else
-                    last_table_hdu="$option_value"
-                fi
 
-                # If this extension of this file is actually a FITS table
-                # (not an image), then set the 'last_table' variable and
-                # break out of the loop that parses the tokens.
-                if $_gnuastro_asttable $token -h$last_table_hdu -i &> 
/dev/null; then
-                    last_table="$token"
-                    break;
-                fi
 
-            # Not a FITS file, check if it is a plaintext table.
-            elif _gnuastro_autocomplete_is_plaintext_table $token; then
-                last_table="$token"
-                break;
-            fi
-        fi
-    done
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#######################################################################
+############               Completion replies              ############
+#######################################################################
+
+# Add completion replies for the values to '--searchin'.
+_gnuastro_autocomplete_compreply_searchin(){
+    for v in name unit comment; do COMPREPLY+=("$v"); done
 }
 
 
 
 
 
-# Accepts a valid FITS file. Then, reads the column names using the
-# asttable program and echoes the resulting STR.
-#
-# Force 'awk' to read after the second line of 'asttable' output,
-# because the second line contains the filename. The filename might
-# start with numbers. If so, there will be an unwanted '(hdu:'
-# printed in the results. Here, 'awk' will print the second column
-# in lines that start with a number.
-_gnuastro_autocomplete_get_fits_columns(){
-    local inputfile="$1"
-    $_gnuastro_asttable --information $inputfile \
-        | awk 'NR>2' \
-        | awk '/^[0-9]/ {print $2}'
+# Add completion replies for the values to '--searchin'.
+_gnuastro_autocomplete_compreply_tableformat(){
+    for v in fits-ascii fits-binary txt; do COMPREPLY+=("$v"); done
 }
 
 
 
 
-# Accept a valid FITS file and suggest its column names.
-_gnuastro_autocomplete_list_fits_columns(){
-    local inputfile="$1"
-    local list=("$(_gnuastro_autocomplete_get_fits_columns "$1")")
-    _gnuastro_autocomplete_compgen "${list[@]}"
+
+# Add matching options to the completion replies.
+_gnuastro_autocomplete_compreply_options_all(){
+
+    # Variable only necessary here.
+    local options_match=""
+
+    # Get the list of option names (with an '=' after those that need a
+    # value (if 'options_all' isn't already set)
+    if [ x"$options_all" = x ]; then
+        _gnuastro_autocomplete_option_list
+    fi
+
+    # Limit the options to those that start with the already given portion.
+    if [ x$1 = x ]; then
+        options_match=$options_all
+    else
+        # We aren't using 'grep' because it can confuse the '--XXX' with
+        # its own options on some systems (and placing a '--' before the
+        # search string may not be portable).
+        options_match=$(echo "$options_all" | awk '/^'$1'/')
+    fi
+
+    # Add the list of options.
+    for f in $options_match; do COMPREPLY+=("$f"); done
+
+    # Disable the extra space on the command-line after the match, only for
+    # this run (only relevant when we have found the match).
+    compopt -o nospace
 }
 
 
 
 
 
-# The last file name (.txt/.fits) in COMP_LINE
-_gnuastro_autocomplete_get_file(){
-    echo "Pass"
+# Add a file into the completion replies. The main point is to remove the
+# fixed directory name prefix of the file (that is appended by 'ls').
+#
+# It takes two arguments:
+#
+#   1. Base string (that was fed into 'ls' to find the full string).
+#      This string CAN BE EMPTY.
+#   2. Full string.
+#
+# If the first is a full directory, then it will remove it from the full
+# string before saving string (which is standard in autocomplete (the user
+# has already given it and it is just annoying!).
+_gnuastro_autocomplete_compreply_file(){
+    if [ x$1 != x ] && [ -d $1 ]; then COMPREPLY+=("${2#$1}")
+    else                               COMPREPLY+=("$2")
+    fi
 }
 
 
 
 
-# Accept the program name and its absolute path, run the --help option and
-# 'append' all long options to the current suggestions. 'Appending' seems a
-# good idea because the program might accept multiple input types. For
-# example the 'asttable' program can either accept a fits file or various
-# short/long options as its first argument. In this case, autocompletion
-# suggests both.
-_gnuastro_autocomplete_list_options(){
-    local input_program="$1"
-    local list=("$($input_program --help \
-                  | awk -v regex=" --+[a-zA-Z0-9]*=?" \
-                        'match($0, regex) \
-                           {print substr($0, RSTART, RLENGTH)}')")
-    _gnuastro_autocomplete_compgen "${list[@]}" "$word"
+
+# Add all the FITS files that contain an image in the location pointed to
+# by first argument into the completion replies.
+_gnuastro_autocomplete_compreply_fits_images(){
+
+    # Get list of matching files.
+    #   with '-d' we are telling 'ls' to not go into sub-directories.
+    local files=($(ls -d "$1"* 2> /dev/null))
+
+    # Parse the list of files and add it when it is a directory or it can
+    # be read as a table.
+    for f in ${files[*]} ; do
+        if [ -d "$f" ]; then COMPREPLY+=("${f#$1}/");
+        elif _gnuastro_autocomplete_fits_has_image "$f"; then
+            _gnuastro_autocomplete_compreply_file "$1" "$f"
+        fi
+    done
 }
 
 
 
 
 
-# Prints the message taken as $1 and asks for user action. Then reprints
-# the former prompt, which lives in $COMP_LINE.
+# Add all the HDUs that contain a table/image in the first argument (a FITS
+# file) into the completion replies.
 #
-# TODO: The newly printed prompt was taken from here:
-# https://stackoverflow.com/questions/22322879/how-to-print-current-bash-prompt
-# This is only available since Bash 4.4 (September 2016). We should find
-# A more low-level/basic operation.
-_gnuastro_autocomplete_print_message(){
-    if ! [ x"$1" = x ]; then
-        printf "\n$1\n"
-        printf "${PS1@P}%s" "$COMP_LINE"
+# INPUT ARGUMENTS
+#    1) Mode of file ('table' or 'image').
+#    2) Name of file.
+_gnuastro_autocomplete_compreply_hdus(){
+    if _gnuastro_autocomplete_is_fits "$2"; then
+        for h in $("$_gnuastro_prefix"/astfits "$2" --list"$1"hdus); do
+            COMPREPLY+=("$h")
+        done
     fi
 }
 
 
 
 
-_gnuastro_asttable_completions(){
-    # Basic definitions.
-    PROG_NAME=$1
 
-    # Initialize the completion response with null
-    COMPREPLY=();
+# Add all the tables in the location pointed to by first argument into the
+# completion replies.
+_gnuastro_autocomplete_compreply_tables(){
 
-    # Strings used in multiple places.
-    local infowarning="With '--information' (or '-i') all other options are 
disabled, you can press ENTER now."
+    # Get list of matching files.
+    #   with '-d' we are telling 'ls' to not go into sub-directories.
+    local files=($(ls -d "$1"* 2> /dev/null))
 
-    # Variable "word", is the current word being completed. "$2" is the
-    # default value for the current word in completion scripts. But we are
-    # using the longer form: "${COMP_WORDS[COMP_CWORD]}" for clarity.
-    word="${COMP_WORDS[COMP_CWORD]}"
-    if [ "$word" = "=" ]; then
-        # The equal sign '=' raises complexities when filling suggestions
-        # for long options. Things will work out fine when they are simply
-        # ignored.
-        word=""
-    fi
+    # Parse the list of files and add it when it is a directory or it can
+    # be read as a table.
+    for f in ${files[*]} ; do
+        if [ -d "$f" ]; then COMPREPLY+=("${f#$1}/");
+        elif _gnuastro_autocomplete_is_table "$f"; then
+            _gnuastro_autocomplete_compreply_file "$1" "$f"
+        fi
+    done
+}
 
-    # Variable "prev", is one word before the one being completed. By
-    # default, this is set as "$3" in completion scripts. But we are using
-    # the longer form: "${COMP_WORDS[COMP_CWORD-1]}" for clarity.
-    prev="${COMP_WORDS[COMP_CWORD-1]}"
-    if [ "$prev" = "=" ]; then
-        # While a user is writing a long option's argument, the previous
-        # word will be the equal sign '='. This is not helpful at all. But
-        # looking at a word just before '=' helps us understand which long
-        # option is being called upon.
-        prev="${COMP_WORDS[COMP_CWORD-2]}"
+
+
+
+
+# Add all table columns in given file (possibly with HDU) that start with
+# the given string into the completion replies.
+_gnuastro_autocomplete_compreply_table_columns(){
+
+    # Inputs
+    local table_file="$1"
+    local table_hdu="$2"
+    local tomatch="$3"
+
+    # Internal
+    local columns=""
+    local hdu_option=""
+
+    # If a HDU is given, then add it to the options.
+    if [ x"$table_hdu" != x ]; then hdu_option="--hdu=$table_hdu"; fi
+
+    # Get the list of columns from the output of '--information': the
+    # column names are the second column of the lines that start with a
+    # number. If there is no column name, print the column number.
+    #
+    # We are forcing 'awk' to read after the second line of 'asttable'
+    # output, because the second line contains the filename. The filename
+    # might start with numbers. If so, there will be an unwanted '(hdu:'
+    # printed in the results. Here, 'awk' will print the second column in
+    # lines that start with a number.
+    columns=$("$_gnuastro_prefix"/asttable --information \
+                                 "$table_file" $hdu_option \
+                  | awk 'NR>2 && /^[0-9]/ { \
+                            if($2=="n/a") print $1; else print $2 \
+                         }' \
+                  | grep ^$tomatch)
+
+    # Add the columns into the completion replies.
+    for c in $columns; do COMPREPLY+=("$c"); done
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#######################################################################
+############         Only for Table (this program)         ############
+#######################################################################
+
+# Dealing with arguments: Table only takes one argument/file. So if a table
+# has been previously given on the command-line only print option names.
+_gnuastro_autocomplete_asttable_arguments(){
+    local given_file=""
+    if _gnuastro_autocomplete_first_in_arguments table; then
+        _gnuastro_autocomplete_compreply_options_all ""
+    else
+        _gnuastro_autocomplete_compreply_tables "$argument"
     fi
+}
 
 
 
 
-    case "$prev" in
-        asttable)
-            _gnuastro_autocomplete_list_fits_names
-            _gnuastro_autocomplete_list_options $PROG_NAME
-            ;;
-        -i|--information)
-            # If a table has been called until this stage, extract it. The 
table
-            # name itself will be put in 'last_table' and its possible HDU (if 
its
-            # was a FITS table), will be put in 'last_table_hdu'.
-            _gnuastro_autocomplete_last_table
-
-            # when a file has been given before this, and the
-            # '--information' option is called, we should tell the user to
-            # avoid trying new options and just press ENTER. Otherwise, we
-            # should let the user choose a FITS table (to view its
-            # information).
-            if [ x"$last_table" = x ]; then
-                _gnuastro_autocomplete_list_all_valid_files
-            else
-                _gnuastro_autocomplete_print_message "$infowarning"
-                COMPREPLY=()
-            fi
+
+# Fill option value (depends on option).
+_gnuastro_autocomplete_asttable_option_value(){
+
+    # Internal variables.
+    local fits_file=""
+    local given_hdu=""
+    local given_file=""
+
+    # Keep this in the same order as the output of '--help', for options
+    # with similar operations, keep the order within the '|'s.
+    case "$option_name" in
+
+        # Options that need a column from the main argument.
+        -b|--noblank|-c|--column|--inpolygon|--outpolygon)
+            _gnuastro_autocomplete_given_file_and_hdu table "" --hdu
+            _gnuastro_autocomplete_compreply_table_columns \
+                "$given_file" "$given_hdu" "$current"
             ;;
-        -L|--catcolumnfile|-w|--wcsfile)
-            # Only suggest a fits filename
-            _gnuastro_autocomplete_list_fits_names
+
+        # Options that take the column name as first component of value.
+        -m|--colmetadata|-e|--equal|-n|--notequal)
+
+            # Get the main argument's name (and possible HDU).
+            _gnuastro_autocomplete_given_file_and_hdu table "" --hdu
+            _gnuastro_autocomplete_compreply_table_columns \
+                "$given_file" "$given_hdu" "$current"
+
+            # Since these options take a column name as first value and the
+            # user should continue with other details, we need to disable
+            # the extra space on the command-line after the successful
+            # match.
+            compopt -o nospace
             ;;
-        -c|--column|-r|--range|-s|--sort|-C|--catcolumns| \
-            -m|--colmetadata|--inpolygon|--outpolygon| \
-            -e|--equal|-n|--notequal|-b|--noblank|--searchin)
-            # The function below returns the columns inside the last fits
-            # file specified in the commandline. If no fits files were
-            # detected, there will be no response from autocompletion. This
-            # might alert the user that something is going wrong.
-            _gnuastro_autocomplete_last_table
-            _gnuastro_autocomplete_list_fits_columns "$last_table"
+
+        -C|--catcolumns)
+            _gnuastro_autocomplete_given_file_and_hdu \
+                table --catcolumnfile --catcolumnhdu
+            _gnuastro_autocomplete_compreply_table_columns \
+                "$given_file" "$given_hdu" "$current"
             ;;
-        -W|--wcshdu|-u|--catcolumnhdu|-h|--hdu)
-            # Description is same as the '--column' option.
-            _gnuastro_autocomplete_last_table
-            _gnuastro_autocomplete_list_fits_hdu "$last_table"
+
+        -h|--hdu)
+            _gnuastro_autocomplete_given_file table ""
+            _gnuastro_autocomplete_compreply_hdus table "$given_file"
             ;;
-        -o|--output|--polygon|-H|--head|-t|--tail| \
-            --onlyversion|-N|--numthreads|--minmapsize)
-            # Do not suggest anything.
+
+        -L|--catcolumnfile)
+            _gnuastro_autocomplete_compreply_tables "$current"
             ;;
-        --config)
-            # Suggest config files
-            COMPREPLY=($(compgen -f -X "!*.[cC][oO][nN][fF]" -- "$word"))
+
+        --searchin)
+            _gnuastro_autocomplete_compreply_searchin
             ;;
-        *)
-            # Check the entire prompt line $COMP_LINE, if the '-i' or
-            # '--information' option is passed and there is a valid fits
-            # file present in the command line, prompt user that they can
-            # safely press ENTER since this configuration disables all
-            # other options. Otherwise, just print all available options.
-            if echo "$COMP_LINE" \
-                    | grep -e ' --information' -e ' -i' &> /dev/null; then
-                _gnuastro_autocomplete_last_table
-                if  [ x"$last_table" ]; then
-                    _gnuastro_autocomplete_print_message "$infowarning"
-                    COMPREPLY=()
-                fi
-            else
-                _gnuastro_autocomplete_list_options $PROG_NAME
-            fi
+
+        -u|--catcolumnhdu)
+            _gnuastro_autocomplete_given_file table --catcolumnfile
+            _gnuastro_autocomplete_compreply_hdus table "$given_file"
             ;;
-    esac
 
-    if [[ "${COMPREPLY[@]}" =~ "=" ]]; then
-        # Do not append 'space' character to the end of line in case there
-        # is a long option present in the suggestions. Please note that
-        # long options mostly have a '=' suffix, for example '--hdu=1'.
-        compopt -o nospace
-    fi
+        -w|--wcsfile)
+            _gnuastro_autocomplete_compreply_fits_images "$current"
+            ;;
 
-    # Be verbose in debugging mode, where $db is set to '0'.
-    if [ $_gnuastro_completion_debug = 0 ]; then
-        cat <<EOF
-
-************ DEBUG ************
->>> prev: '$prev' -- \$3: '$3'
->>> word: '$word' -- \$2: '$2'
->>> last_table: '$last_table'
->>> PROG_NAME: $PROG_NAME
->>> COMP_WORDS: '${COMP_WORDS[@]}'
->>> COMPREPLY: '${COMPREPLY[@]}'
-*******************************
-EOF
-        # Printf should stay outside the 'heredoc' above to prevent an
-        # extra newline. As a result, the cursor stays on the same line.
-        printf ">>> Line: %s" "$COMP_LINE"
-    fi
+        -W|--wcshdu)
+            _gnuastro_autocomplete_given_file image --wcsfile
+            _gnuastro_autocomplete_compreply_hdus image "$given_file"
+            ;;
 
+        --tableformat)
+            _gnuastro_autocomplete_compreply_tableformat
+            ;;
+    esac
 }
 
 
 
 
-# Define the completion specification, or COMPSPEC: -o bashdefault: Use
-# Bash default completions if nothing is found.  -F function: Use this
-# 'function' to generate the given program's completion.
-complete -o bashdefault -F _gnuastro_asttable_completions asttable
-
-
-
-
 
+_gnuastro_autocomplete_asttable(){
 
+    # Basic initialization. The variables we want to remain inside this
+    # function are given a 'local' here and set inside the 'initialize'
+    # function. The variables are defined above the function that gives
+    # them a value.
+    local prev=""
+    local current=""
+    local argument=""
+    _gnuastro_autocomplete_initialize
 
+    # For a check
+    #echo
+    #echo "prev:     $prev"
+    #echo "current:  $current"
+    #echo "argument: $argument"
 
-# A though on reporing errors, e.g. invalid filenames. The autocompletion
-# feature should not suggest anything in case there is an error in the
-# commandline. No errors or messages should be shown as the program in
-# charge will handle that. This autocompletion feature is only here to help
-# with preventing unintentional mistakes. So, in case there is an invalid
-# command on the current commandline, there should be no completion
-# suggestions.
-
+    # Extract the current mode (if the user is giving an argument, option
+    # name, or option value). See the description above this function on
+    # how the mode is set.
+    local options_all=""
+    local option_name=""
+    local option_value=""
+    local option_name_complete=0
+    _gnuastro_autocomplete_mode
+
+    # For a check
+    #echo
+    #echo "argument:             $argument"
+    #echo "option_name:          $option_name"
+    #echo "option_name_complete: $option_name_complete"
+    #echo "option_value:         $option_value"
+
+    # If 'option_name_complete==1', then we are busy filling in the option
+    # value.
+    if [ $option_name_complete = 1 ]; then
+        _gnuastro_autocomplete_asttable_option_value
+
+    # When 'option_name' is not empty (and not yet complete), we are busy
+    # filling in the option name.
+    elif [ x$option_name != x ]; then
+        _gnuastro_autocomplete_compreply_options_all "$option_name"
+
+    # In the case of "none-of-the-above", it is an argument.
+    else
+        _gnuastro_autocomplete_asttable_arguments
+    fi
+}
 
 
 
 
-# Use extended globs in the case statements if needed
-# https://mywiki.wooledge.org/BashGuide/Patterns#Extended_Globs
-# shopt -s extglob
 
-#  astquery gaia --dataset=edr3 --center=24,25 --radius=0.1 --output=gaia.fits 
--column=ra,dec,parallax --quiet -i | awk '/[0-9]+/ {print $2}'
+# Define the completion specification, or COMPSPEC: -o bashdefault: Use
+# Bash default completions if nothing is found.  -F function: Use this
+# 'function' to generate the given program's completion.
+complete -o bashdefault -F _gnuastro_autocomplete_asttable asttable



reply via email to

[Prev in Thread] Current Thread [Next in Thread]