Repository: pallets/click

Issue:
Allow sorting of completions based on custom types
Currently completion candidates returned by click are sorted in ZSH based on alphanumeric rules. This means that completions ['1', '2', '10'] would be displayed out of natural sort order as ['1', '10', '2']. Update the completion script to bring the sorting into Python where custom types can be sorted more appropriately. Note that the version of bash that ships with OS X is v3.2. The nosort option for `complete` was introduced in v4.4 This change should support both.


Discussion:

Resolving patch:
diff --git a/click/_bashcomplete.py b/click/_bashcomplete.py
--- a/click/_bashcomplete.py
+++ b/click/_bashcomplete.py
@@ -10,6 +10,7 @@
 
 WORDBREAK = '='
 
+# Note, only BASH version 4.4 and later have the nosort option.
 COMPLETION_SCRIPT_BASH = '''
 %(complete_func)s() {
     local IFS=$'\n'
@@ -19,7 +20,17 @@
     return 0
 }
 
-complete -F %(complete_func)s %(script_names)s
+%(complete_func)setup() {
+    local COMPLETION_OPTIONS=""
+    local BASH_VERSION_ARR=(${BASH_VERSION//./ })
+    if [ ${BASH_VERSION_ARR[0]} -ge 4 ] && [ ${BASH_VERSION_ARR[1]} -ge 4 ];then
+        COMPLETION_OPTIONS="-o nosort"
+    fi
+
+    complete $COMPLETION_OPTIONS -F %(complete_func)s %(script_names)s
+}
+
+%(complete_func)setup
 '''
 
 COMPLETION_SCRIPT_ZSH = '''
@@ -41,11 +52,13 @@
     done
 
     if [ -n "$completions_with_descriptions" ]; then
-        _describe '' completions_with_descriptions
+        _describe -V unsorted completions_with_descriptions -U -Q
     fi
+
     if [ -n "$completions" ]; then
-        compadd -M 'r:|=* l:|=* r:|=*' -a completions
+        compadd -U -V unsorted -Q -a completions
     fi
+    compstate[insert]="automenu"
 }
 
 compdef %(complete_func)s %(script_names)s
@@ -232,7 +245,8 @@ def get_choices(cli, prog_name, args, incomplete):
             return get_user_autocompletions(ctx, all_args, incomplete, param)
 
     add_subcommand_completions(ctx, incomplete, completions)
-    return completions
+    # Sort before returning so that proper ordering can be enforced in custom types.
+    return sorted(completions)
 
 
 def do_complete(cli, prog_name, include_descriptions):
diff --git a/examples/bashcompletion/bashcompletion.py b/examples/bashcompletion/bashcompletion.py
--- a/examples/bashcompletion/bashcompletion.py
+++ b/examples/bashcompletion/bashcompletion.py
@@ -8,6 +8,7 @@ def cli():
 
 
 def get_env_vars(ctx, args, incomplete):
+    # Completions returned as strings do not have a description displayed.
     for key in os.environ.keys():
         if incomplete in key:
             yield key
@@ -26,11 +27,13 @@ def group():
 
 
 def list_users(ctx, args, incomplete):
-    # Here you can generate completions dynamically
-    users = ['bob', 'alice']
-    for user in users:
-        if user.startswith(incomplete):
-            yield user
+    # You can generate completions with descriptions by returning
+    # tuples in the form (completion, description).
+    users = [('bob', 'butcher'),
+             ('alice', 'baker'),
+             ('jerry', 'candlestick maker')]
+    # Ths will allow completion matches based on matches within the description string too!
+    return [user for user in users if incomplete in user[0] or incomplete in user[1]]
 
 
 @group.command(help='Choose a user')
@@ -38,4 +41,5 @@ def list_users(ctx, args, incomplete):
 def subcmd(user):
     click.echo('Chosen user is %s' % user)
 
+
 cli.add_command(group)


Test patch: