Skip to content

Commit

Permalink
Merge pull request #1423 from jle64/fish_complete
Browse files Browse the repository at this point in the history
Add support for fish completion
  • Loading branch information
davidism authored Mar 5, 2020
2 parents 3592995 + 6bf782e commit 433708e
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 67 deletions.
3 changes: 2 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ Unreleased
showing the name for friendlier debugging. :issue:`1267`
- Completion doesn't consider option names if a value starts with
``-`` after the ``--`` separator. :issue:`1247`
- ZSH Completion escapes special characters in values. :pr:`1418`
- ZSH completion escapes special characters in values. :pr:`1418`
- Add completion support for Fish shell. :pr:`1423`


Version 7.0
Expand Down
47 changes: 41 additions & 6 deletions click/_bashcomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,22 @@
compdef %(complete_func)s %(script_names)s
'''

COMPLETION_SCRIPT_FISH = '''
complete --no-files --command %(script_names)s --arguments "(env %(autocomplete_var)s=complete_fish COMP_WORDS=(commandline -cp) COMP_CWORD=(commandline -t) %(script_names)s)"
'''

_completion_scripts = {
"bash": COMPLETION_SCRIPT_BASH,
"zsh": COMPLETION_SCRIPT_ZSH,
"fish": COMPLETION_SCRIPT_FISH,
}

_invalid_ident_char_re = re.compile(r'[^a-zA-Z0-9_]')


def get_completion_script(prog_name, complete_var, shell):
cf_name = _invalid_ident_char_re.sub('', prog_name.replace('-', '_'))
script = COMPLETION_SCRIPT_ZSH if shell == 'zsh' else COMPLETION_SCRIPT_BASH
script = _completion_scripts.get(shell, COMPLETION_SCRIPT_BASH)
return (script % {
'complete_func': '_%s_completion' % cf_name,
'script_names': prog_name,
Expand Down Expand Up @@ -283,17 +293,42 @@ def do_complete(cli, prog_name, include_descriptions):
for item in get_choices(cli, prog_name, args, incomplete):
echo(item[0])
if include_descriptions:
# ZSH has trouble dealing with empty array parameters when returned from commands, so use a well defined character '_' to indicate no description is present.
# ZSH has trouble dealing with empty array parameters when
# returned from commands, use '_' to indicate no description
# is present.
echo(item[1] if item[1] else '_')

return True


def do_complete_fish(cli, prog_name):
cwords = split_arg_string(os.environ["COMP_WORDS"])
incomplete = os.environ["COMP_CWORD"]
args = cwords[1:]

for item in get_choices(cli, prog_name, args, incomplete):
if item[1]:
echo("%(arg)s\t%(desc)s" % {"arg": item[0], "desc": item[1]})
else:
echo(item[0])

return True


def bashcomplete(cli, prog_name, complete_var, complete_instr):
if complete_instr.startswith('source'):
shell = 'zsh' if complete_instr == 'source_zsh' else 'bash'
if "_" in complete_instr:
command, shell = complete_instr.split("_", 1)
else:
command = complete_instr
shell = "bash"

if command == "source":
echo(get_completion_script(prog_name, complete_var, shell))
return True
elif complete_instr == 'complete' or complete_instr == 'complete_zsh':
return do_complete(cli, prog_name, complete_instr == 'complete_zsh')
elif command == "complete":
if shell == "fish":
return do_complete_fish(cli, prog_name)
elif shell in {"bash", "zsh"}:
return do_complete(cli, prog_name, shell == "zsh")

return False
152 changes: 92 additions & 60 deletions docs/bashcomplete.rst
Original file line number Diff line number Diff line change
@@ -1,47 +1,46 @@
Bash Complete
=============
Shell Completion
================

.. versionadded:: 2.0

As of Click 2.0, there is built-in support for Bash completion for
any Click script. There are certain restrictions on when this completion
is available, but for the most part it should just work.
Click can provide tab completion for commands, options, and choice
values. Bash, Zsh, and Fish are supported

Limitations
-----------
Completion is only available if a script is installed and invoked
through an entry point, not through the ``python`` command. See
:ref:`setuptools-integration`.

Bash completion is only available if a script has been installed properly,
and not executed through the ``python`` command. For information about
how to do that, see :ref:`setuptools-integration`. Click currently
only supports completion for Bash and Zsh.

What it Completes
-----------------

Generally, the Bash completion support will complete subcommands,
Generally, the shell completion support will complete commands,
options, and any option or argument values where the type is
:class:`click.Choice`. Subcommands and choices are always listed whereas
options are only listed if at least a dash has been provided. ::
:class:`click.Choice`. Options are only listed if at least a dash has
been entered.

.. code-block:: text
$ repo <TAB><TAB>
clone commit copy delete setuser
$ repo clone -<TAB><TAB>
--deep --help --rev --shallow -r
Additionally, custom suggestions can be provided for arguments and options with
the ``autocompletion`` parameter. ``autocompletion`` should a callback function
that returns a list of strings. This is useful when the suggestions need to be
dynamically generated at bash completion time. The callback function will be
passed 3 keyword arguments:
Custom completions can be provided for argument and option values by
providing an ``autocompletion`` function that returns a list of strings.
This is useful when the suggestions need to be dynamically generated
completion time. The callback function will be passed 3 keyword
arguments:

- ``ctx`` - The current click context.
- ``args`` - The list of arguments passed in.
- ``incomplete`` - The partial word that is being completed, as a string. May
be an empty string ``''`` if no characters have been entered yet.
- ``ctx`` - The current command context.
- ``args`` - The list of arguments passed in.
- ``incomplete`` - The partial word that is being completed. May
be an empty string if no characters have been entered yet.

Here is an example of using a callback function to generate dynamic suggestions:
Here is an example of using a callback function to generate dynamic
suggestions:

.. click:example::
.. code-block:: python
import os
Expand All @@ -55,25 +54,26 @@ Here is an example of using a callback function to generate dynamic suggestions:
click.echo('Value: %s' % os.environ[envvar])
Completion help strings (ZSH only)
----------------------------------
Completion help strings
-----------------------

ZSH supports showing documentation strings for completions. These are taken
from the help parameters of options and subcommands. For dynamically generated
completions a help string can be provided by returning a tuple instead of a
string. The first element of the tuple is the completion and the second is the
help string to display.
ZSH and fish support showing documentation strings for completions.
These are taken from the help parameters of options and subcommands. For
dynamically generated completions a help string can be provided by
returning a tuple instead of a string. The first element of the tuple is
the completion and the second is the help string to display.

Here is an example of using a callback function to generate dynamic suggestions with help strings:
Here is an example of using a callback function to generate dynamic
suggestions with help strings:

.. click:example::
.. code-block:: python
import os
def get_colors(ctx, args, incomplete):
colors = [('red', 'help string for the color red'),
('blue', 'help string for the color blue'),
('green', 'help string for the color green')]
colors = [('red', 'a warm color'),
('blue', 'a cool color'),
('green', 'the other starter color')]
return [c for c in colors if incomplete in c[0]]
@click.command()
Expand All @@ -85,47 +85,79 @@ Here is an example of using a callback function to generate dynamic suggestions
Activation
----------

In order to activate Bash completion, you need to inform Bash that
completion is available for your script, and how. Any Click application
automatically provides support for that. The general way this works is
through a magic environment variable called ``_<PROG_NAME>_COMPLETE``,
where ``<PROG_NAME>`` is your application executable name in uppercase
with dashes replaced by underscores.
In order to activate shell completion, you need to inform your shell
that completion is available for your script. Any Click application
automatically provides support for that. If the program is executed with
a special ``_<PROG_NAME>_COMPLETE`` variable, the completion mechanism
is triggered instead of the normal command. ``<PROG_NAME>`` is the
executable name in uppercase with dashes replaced by underscores.

If your tool is called ``foo-bar``, then the variable is called
``_FOO_BAR_COMPLETE``. By exporting it with the ``source_{shell}``
value it will output the activation script to evaluate.

Here are examples for a ``foo-bar`` script.

If your tool is called ``foo-bar``, then the magic variable is called
``_FOO_BAR_COMPLETE``. By exporting it with the ``source`` value it will
spit out the activation script which can be trivially activated.
For Bash, add this to ``~/.bashrc``:

For instance, to enable Bash completion for your ``foo-bar`` script, this
is what you would need to put into your ``.bashrc``::
.. code-block:: text
eval "$(_FOO_BAR_COMPLETE=source foo-bar)"
eval "$(_FOO_BAR_COMPLETE=source_bash foo-bar)"
For zsh users add this to your ``.zshrc``::
For Zsh, add this to ``~/.zshrc``:

.. code-block:: text
eval "$(_FOO_BAR_COMPLETE=source_zsh foo-bar)"
From this point onwards, your script will have autocompletion enabled.
For Fish, add this to ``~/.config/fish/completions/foo-bar.fish``:

.. code-block:: text
eval (env _FOO_BAR_COMPLETE=source_fish foo-bar)
Open a new shell to enable completion. Or run the ``eval`` command
directly in your current shell to enable it temporarily.


Activation Script
-----------------

The above activation example will always invoke your application on
startup. This might be slowing down the shell activation time
significantly if you have many applications. Alternatively, you could also
ship a file with the contents of that, which is what Git and other systems
are doing.
The above ``eval`` examples will invoke your application every time a
shell is started. This may slow down shell startup time significantly.

Alternatively, export the generated completion code as a static script
to be executed. You can ship this file with your builds; tools like Git
do this. At least Zsh will also cache the results of completion files,
but not ``eval`` scripts.

This can be easily accomplished::
For Bash:

_FOO_BAR_COMPLETE=source foo-bar > foo-bar-complete.sh
.. code-block:: text
For zsh:
_FOO_BAR_COMPLETE=source_bash foo-bar > foo-bar-complete.sh
For Zsh:

.. code-block:: text
_FOO_BAR_COMPLETE=source_zsh foo-bar > foo-bar-complete.sh
For Fish:

.. code-block:: text
_FOO_BAR_COMPLETE=source_zsh foo-bar > foo-bar-complete.sh
And then you would put this into your .bashrc or .zshrc instead::
In ``.bashrc`` or ``.zshrc``, source the script instead of the ``eval``
command:

.. code-block:: text
. /path/to/foo-bar-complete.sh
For Fish, add the file to the completions directory:

.. code-block:: text
_FOO_BAR_COMPLETE=source_fish foo-bar > ~/.config/fish/completions/foo-bar-complete.fish

0 comments on commit 433708e

Please sign in to comment.