4

Steps to reproduce

  • Create directory on tmp, add 1 file inside.

    mkdir /tmp/testdir && touch /tmp/testdir/examplefile
    
  • Paste below script on /tmp/completion.sh

    # BEGIN AUTOCOMPLETE
    function _foo_complete() {
    local comnum cur opts
    [[ "${COMP_WORDS[@]}" == *"-"* ]] && comnum=2 || comnum=1;
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    opts="--help --restart -h -r"
    if (( COMP_CWORD > comnum )); then
     COMPREPLY=( $(for filename in "/tmp/testdir/"*; do echo ${filename##*/}; done) )
     return
    fi
    if [[ ${cur} == -* ]]; then
     COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
     return 0
    else
     COMPREPLY=( $(for filelist in /tmp/testdir/"$2"*; do echo ${filelist##*/}; done) )
    fi
    }
    complete -F _foo_complete foo.sh
    # END AUTOCOMPLETE
    
  • Source it then.

    . /tmp/completion.sh
    

Expected result

$ foo.sh --restart examplefile <tab><tab>
examplefile           
$ foo.sh --restart examplefile <tab><tab>
examplefile        
$ foo.sh --restart examplefile <tab><tab>
examplefile     
$ foo.sh --restart examplefile <tab><tab>

What happen instead

$ foo.sh --restart examplefile <tab><tab> examplefile <tab><tab> examplefile <tab><tab> examplefile <tab><tab> examplefile <tab><tab> examplefile

I want the suggestion to appear as possible completion, but without actually completing it (for display purposes). This question has been asked before, but to this date no answer is given.

Regarding -o nosort

I look into that option and it's only available at Bash 4.4+, I tried on my 16.04 machine and it fail. Looking for more globalish solution

Liso
  • 188
  • 2
  • 14
  • Do you expect it to always just list all the files in the current directory? i.e. if you type `./myscript /usr/bin<` it shouldn't list `/usr` or `/usr/bin`, but rather the current directory? – root Dec 31 '21 at 04:42
  • @root I have updated the question. The completion file is created to only take file from hard coded directory. – Liso Dec 31 '21 at 10:48
  • Why is there a bounty on this question? @pynexj hack that adds a blank to `COMPREPLY` (which could also be the empty string `''` instead) should be good enough no? – Fravadona Jan 04 '22 at 22:46
  • @Frevadona I forgot to mention, `-o nosort` is only available at Bash 4.4+, I tried on my 16.04 machine and it fail. Looking for more "global"ish solution. – Liso Jan 04 '22 at 23:56
  • Why do you insist on relying on COMPREPLY when you know COMPREPLY will always allow anything that user "tabs" against its values to do a completion? Shouldn't you be producing an output instead? – konsolebox Jan 05 '22 at 01:46
  • @konsolebox That's okay, but can you show how to exactly do that ? At the moment I can't think any action that will show directory content by pressing tab – Liso Jan 05 '22 at 02:33
  • @konsolebox -- he does not want a command to "output" a list of files or something. he wants the files to be automatically "displayed" (but without actually completing) when pressing TAB (in an interactive shell) as in `some-command `. – pynexj Jan 05 '22 at 04:01
  • @Liso you may try my workaround – Fravadona Jan 08 '22 at 01:51

3 Answers3

3

You can try this trick; depending on your OS, this might work for you:

note: I stripped the completion function down to the relevant TabTab functionality

#!/bin/bash
mkdir -p /tmp/testdir && touch /tmp/testdir/examplefile

_foo_complete() {
    local dirpath=/tmp/testdir
    local nullglob=$( shopt -q nullglob && echo true || echo false )

    $nullglob || shopt -s nullglob
    COMPREPLY=( "$dirpath"/* )
    $nullglob || shopt -u nullglob

    COMPREPLY=( "${COMPREPLY[@]#"$dirpath"/}" )

    [ ${#COMPREPLY[@]} -eq 1 ] && COMPREPLY+=( $'\xC2\xA0' )
}
complete -F _foo_complete foo.sh

Postscript:

It's basically the same idea than @pynexj answer: adding a second, invisible, element to COMPREPLY, thus disabling the Tab auto-completion while keeping the TabTab listing.

Now the problem is that bash sorts COMREPLY automatically, so, with the ASCII space character being numerically the first printable character, it will be displayed as the first column of the listing.

To work around it I tried almost all other white-space characters in the UTF-8 charset and they all get sorted to the same rank than the ASCII space character; all but the NON-BREAKING-SPACE, that's what I'm using here.

The fix is not full proof because there's a lot of UTF-8 characters that will get sorted after it, and above all, it gets different treatments depending on the OS: some sort implementations will give it the same rank than the ASCII space character, while others won't. Think about it, what is NON-BREAKING-SPACE conceptually? It's a white-space, but also like a letter at the same time!

Fravadona
  • 13,917
  • 1
  • 23
  • 35
  • I can't get your solution to work, could you test your script with my requirement/code I posted in my question ? However that html encoding it's working nicely. – Liso Jan 08 '22 at 04:39
  • `0xc2a0` is utf8 [non-breaking space](https://en.wikipedia.org/wiki/Non-breaking_space) so the idea is similar. not sure if it works in non-utf8 locale and not sure if it would always be the last one when being sorted. – pynexj Jan 08 '22 at 05:40
  • @pynexj: Yeah, I've been trying different approaches and I now can say that there is no other way to get the expected output. Using ` ` have its limitations, but as long as the filename starts with an ASCII character and the OS supports UTF8 then it will work. – Fravadona Jan 08 '22 at 09:29
  • @Liso The code was only illustrating the TAB TAB completion. I'll update it for implementing the functionalities of the original function (not possible today though) – Fravadona Jan 08 '22 at 09:38
  • just tried but for me (`en_US.UTF-8`) the nbsp also comes first when displaying the completion candidates. what locale are you using? – pynexj Jan 08 '22 at 09:53
  • you can also try with `printf '%s\n' aaa zzz $'\xc2\xa0' | LC_ALL=en_US.UTF-8 sort`. – pynexj Jan 08 '22 at 09:53
  • @pynexj I tried LANG and LC_ALL with `C` `en_US.UTF-8` `fr_FR.UTF-8` `es_ES.UTF-8`, all of them are working fine – Fravadona Jan 08 '22 at 11:34
  • maybe it's OS dependent. my result (on macos Big Sur) is not the same. see https://i.stack.imgur.com/AsoTC.png – pynexj Jan 08 '22 at 11:48
  • 3
    @pynexj It seems that the sorting is different from OS to OS. Using `en_US-utf-8` as LANG/LC_ALL, it works on macOS Mojave/Catalina, FreeBSD 11/12, RedHat 7/8, but **not** on Solaris 11.3/11.4 and macOS BigSur – Fravadona Jan 08 '22 at 12:30
1

Try the following compspec.sh (based on OP's code):

function _foo_complete()
{
    local comnum cur opts

    [[ "${COMP_WORDS[@]}" == *"-"* ]] && comnum=2 || comnum=1;

    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    opts="--help --restart -h -r"

    if (( COMP_CWORD > comnum )); then
        COMPREPLY=( $(for filename in "/tmp/testdir/"*; do echo ${filename##*/}; done) )
        if [[ ${#COMPREPLY[@]} -gt 0 ]]; then
            #
            # you can try COMPREPLY+=( zzz ) and see what's happening
            #
            COMPREPLY+=( ' ' )
        fi
        return
    fi

    if [[ ${cur} == -* ]]; then
        COMPREPLY=( $( compgen -W "${opts}" -- ${cur} ) )
        return 0
    else
        COMPREPLY=( $( for filelist in /tmp/testdir/"$2"*; do echo ${filelist##*/}; done ) )
    fi
}

complete -F _foo_complete foo.sh

enter image description here


UPDATE:

is it possible to move the empty string to end of completion, so it doesn't look like there are empty space?

You can use complete -o nosort (requires Bash 4.4+) if you can sort the completion candidates all by yourself.

pynexj
  • 19,215
  • 5
  • 38
  • 56
  • Hello, it works great. If it's not too much to ask, how to make response/possible completion aligned to left and not center ? Thanks – Liso Jan 01 '22 at 18:15
  • nothing is perfect. it's good enough to be good enough. – pynexj Jan 01 '22 at 18:24
  • Then is it possible to move the empty string to end of completion, so it doesn't look like there are empty space? Ive been looking for that – Liso Jan 02 '22 at 05:14
0

Edited code corresponding to your complete requirements:

_test(){
   COMPREPLY=(                                                                                                                      
      $( [[ "$2" == -* ]] && compgen -W'-a -ab -c' -- "$2" \                                                                
      || [[ -d test ]] && compgen -W'$(basename -a test/*)' -- "$2" | grep -v -w -f <(printf "%s\n" "${COMP_WORDS[@    ]}:1:((COMP_CWORD-1))"))
   )
}

if the test directory doesn't exist completion would work with current directory contents so we have to check it

options can be used multiple times as you didn't specify it. 

1 2 complete -F _test test

  • 1. Yes, no duplicate file/option on argument; 2. Also yes, if user wish to include options; 3. File from current directory should be presented upon pressing `tab`. – Liso Jan 06 '22 at 10:07
  • it's important to get the requirements right or it's wild goose chase, 1 and 3 were absent, and 2 is not defined enough; should 2 be active when the user starts an argument with '-' or should those option be listed with the possible files, not duplicated, from the current directory and the specific directory? should the options be listed first or sorted like the default completion does? – Nadim Khemir Jan 07 '22 at 12:28
  • If user start argument with `-`, it should bring argument `--restart` `--help` without listing possible files, if `-` is not present-- then list all (no duplicate/repeated) possible files. Meaning if user already pick `file1`, then on next option he shall not pick that again. – Liso Jan 07 '22 at 14:15
  • just to be 100% sure, when you wrote files from the "current directory" did you mean from the current directory plus the test/ directory, only the former? only the later? – Nadim Khemir Jan 07 '22 at 14:36
  • The latter, only from test directory – Liso Jan 07 '22 at 14:44
  • I get the following testing your snippets: `foo.sh --bash: @ : syntax error: operand expected (error token is "@ ")`. – Liso Jan 09 '22 at 07:38
  • Either you copied it wrong https://pastebin.com/2xAA4qSR or it can be bash version related, mine is: GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu) Copyright (C) 2019 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later – Nadim Khemir Jan 10 '22 at 12:30