2016-06-02 12:08:13 -04:00
|
|
|
.. _tutorial_completers:
|
|
|
|
|
|
|
|
*************************************
|
|
|
|
Tutorial: Programmable Tab-Completion
|
|
|
|
*************************************
|
|
|
|
|
|
|
|
Overview
|
|
|
|
================================
|
|
|
|
|
|
|
|
As with many other shells, xonsh ships with the ability to complete
|
|
|
|
partially-specified arguments upon hitting the "tab" key.
|
|
|
|
|
|
|
|
In Python-mode, pressing the "tab" key will complete based on the variable
|
2016-06-02 14:28:29 -04:00
|
|
|
names in the current builtins, globals, and locals, as well as xonsh language
|
2016-06-02 12:08:13 -04:00
|
|
|
keywords & operators, files & directories, and environment variable names. In
|
2016-06-02 14:28:29 -04:00
|
|
|
subprocess-mode, xonsh additionally completes based on the names of any
|
2016-06-04 15:36:21 -04:00
|
|
|
executable files on your $PATH, alias keys, and full Bash completion for the
|
2016-06-02 14:28:29 -04:00
|
|
|
commands themselves.
|
2016-06-02 12:08:13 -04:00
|
|
|
|
|
|
|
xonsh also provides a mechanism by which the results of a tab completion can be
|
|
|
|
customized (i.e., new completions can be generated, or a subset of the built-in
|
|
|
|
completions can be ignored).
|
|
|
|
|
|
|
|
This page details the internal structure of xonsh's completion system and
|
|
|
|
includes instructions for implementing new tab completion functions.
|
|
|
|
|
|
|
|
|
|
|
|
Structure
|
|
|
|
==========
|
|
|
|
|
|
|
|
xonsh's built-in completers live in the ``xonsh.completers`` package, and they
|
2018-09-30 13:59:08 -07:00
|
|
|
are managed through an instance of ``OrderedDict`` (``__xonsh__.completers``)
|
2016-06-02 12:08:13 -04:00
|
|
|
that maps unique identifiers to completion functions.
|
|
|
|
|
2021-06-05 19:12:58 +03:00
|
|
|
The completers are divided to **exclusive** completers and **non-exclusive** completers.
|
|
|
|
Non-exclusive completers are used for completions that are relevant but don't cover the whole completions needed
|
|
|
|
(e.g. completions for the built-in commands ``and``/``or``).
|
|
|
|
|
2016-06-02 12:08:13 -04:00
|
|
|
When the "tab" key is pressed, xonsh loops over the completion functions in
|
2021-06-05 19:12:58 +03:00
|
|
|
order, calling each one in turn and collecting its output until it reaches an **exclusive** one that returns a non-empty
|
|
|
|
set of completions for the current line. The collected completions are then displayed to the
|
2016-06-02 12:08:13 -04:00
|
|
|
user.
|
|
|
|
|
|
|
|
|
|
|
|
Listing Active Completers
|
|
|
|
=========================
|
|
|
|
|
|
|
|
A list of the active completers can be viewed by running the
|
2016-06-04 16:58:46 -04:00
|
|
|
``completer list`` command. This command will display names and descriptions
|
2016-06-02 12:08:13 -04:00
|
|
|
of the currently-active completers, in the order in which they will be
|
|
|
|
checked.
|
|
|
|
|
|
|
|
|
|
|
|
Writing a New Completer
|
|
|
|
=======================
|
|
|
|
|
2021-06-05 19:12:58 +03:00
|
|
|
Completers are implemented as Python functions that take a :class:`Completion Context <xonsh.parsers.completion_context.CompletionContext>` object.
|
|
|
|
Examples for the context object:
|
2016-06-02 12:08:13 -04:00
|
|
|
|
2021-06-05 19:12:58 +03:00
|
|
|
.. code-block:: python
|
2016-06-02 12:08:13 -04:00
|
|
|
|
2021-06-05 19:12:58 +03:00
|
|
|
# ls /tmp/<TAB>
|
|
|
|
CompletionContext(
|
|
|
|
command=CommandContext(
|
|
|
|
args=(CommandArg(value='ls'),),
|
|
|
|
arg_index=1, prefix='/tmp/',
|
|
|
|
),
|
|
|
|
python=PythonContext(multiline_code="ls /tmp/", cursor_index=8, ctx={...})
|
|
|
|
)
|
|
|
|
|
|
|
|
# ls $(whic<TAB> "python") -l
|
|
|
|
CompletionContext(
|
|
|
|
command=CommandContext(
|
|
|
|
args=(CommandArg(value='python', opening_quote='"', closing_quote='"'),),
|
|
|
|
arg_index=0, prefix='whic', subcmd_opening='$(',
|
|
|
|
),
|
|
|
|
python=None
|
|
|
|
)
|
|
|
|
|
|
|
|
# echo @(sys.exe<TAB>)
|
|
|
|
CompletionContext(
|
|
|
|
command=None,
|
|
|
|
python=PythonContext(
|
|
|
|
multiline_code="sys.exe", cursor_index=7,
|
|
|
|
is_sub_expression=True, ctx={...},
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
.. note::
|
|
|
|
Xonsh still supports legacy completers - see `Legacy Completers Support`_.
|
|
|
|
For backwards-compatibility, contextual completers need to be marked (as seen in the examples).
|
|
|
|
|
|
|
|
This function should return a python set of possible completions for ``command.prefix``
|
2016-06-02 12:08:13 -04:00
|
|
|
in the current context. If the completer should not be used in this case, it
|
|
|
|
should return ``None`` or an empty set, which will cause xonsh to move on and
|
|
|
|
try to use the next completer.
|
|
|
|
|
|
|
|
Occasionally, completers will need to return a match that does not actually
|
|
|
|
start with ``prefix``. In this case, a completer should instead return a tuple
|
|
|
|
``(completions, prefixlength)``, where ``completions`` is the set of
|
|
|
|
appropriate completions, and ``prefixlength`` is the number of characters in
|
|
|
|
``line`` that should be treated as part of the completion.
|
|
|
|
|
2021-06-05 19:12:58 +03:00
|
|
|
.. note::
|
|
|
|
Further completion customizations can be made using the ``RichCompletion`` object - see `Advanced Completions`_.
|
|
|
|
|
2016-06-02 12:08:13 -04:00
|
|
|
The docstring of a completer should contain a brief description of its
|
2016-06-04 16:58:46 -04:00
|
|
|
functionality, which will be displayed by ``completer list``.
|
2016-06-02 12:08:13 -04:00
|
|
|
|
2021-06-05 19:12:58 +03:00
|
|
|
Some simple examples follow. For more examples, see the source code of the completers
|
2016-06-02 12:08:13 -04:00
|
|
|
xonsh actually uses, in the ``xonsh.completers`` module.
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
2021-06-05 19:12:58 +03:00
|
|
|
# Helper decorators for completers:
|
|
|
|
from xonsh.completers.tools import *
|
|
|
|
|
|
|
|
@contextual_completer
|
|
|
|
def dummy_completer(context):
|
2016-06-02 12:08:13 -04:00
|
|
|
'''
|
|
|
|
Completes everything with options "lou" and "carcolh",
|
|
|
|
regardless of the value of prefix.
|
|
|
|
'''
|
|
|
|
return {"lou", "carcolh"}
|
2021-06-05 19:12:58 +03:00
|
|
|
|
|
|
|
@non_exclusive_completer
|
|
|
|
@contextual_completer
|
|
|
|
def nx_dummy_completer(context):
|
|
|
|
'''
|
|
|
|
Like dummy_completer but its results are ADDED to the other completions.
|
|
|
|
'''
|
|
|
|
return {"lou", "carcolh"}
|
|
|
|
|
|
|
|
@contextual_completer
|
|
|
|
def python_context_completer(context):
|
2016-06-02 12:08:13 -04:00
|
|
|
'''
|
|
|
|
Completes based on the names in the current Python environment
|
|
|
|
'''
|
2021-06-05 19:12:58 +03:00
|
|
|
if context.python:
|
|
|
|
last_name = context.python.prefix.split()[-1]
|
|
|
|
return {i for i in context.python.ctx if i.startswith(last_name)}
|
2016-06-02 12:08:13 -04:00
|
|
|
|
2021-06-05 19:12:58 +03:00
|
|
|
@contextual_completer
|
|
|
|
def unbeliever_completer(context):
|
2016-06-02 12:08:13 -04:00
|
|
|
'''
|
2019-08-18 17:30:45 +02:00
|
|
|
Replaces "lou carcolh" with "snail" if tab is pressed after at least
|
|
|
|
typing the "lou " part.
|
2016-06-02 12:08:13 -04:00
|
|
|
'''
|
2021-06-05 19:12:58 +03:00
|
|
|
if (
|
|
|
|
# We're completing a command
|
|
|
|
context.command and
|
|
|
|
# We're completing the second argument
|
|
|
|
context.command.arg_index == 1 and
|
|
|
|
# The first argument is 'lou'
|
|
|
|
context.command.args[0].value == 'lou' and
|
|
|
|
# The prefix startswith 'carcolh' (may be empty)
|
|
|
|
'carcolh'.startswith(context.command.prefix)
|
|
|
|
):
|
|
|
|
return {'snail'}, len('lou ') + len(context.command.prefix)
|
|
|
|
|
|
|
|
# Save boilerplate with this helper decorator:
|
|
|
|
|
|
|
|
@contextual_command_completer_for("lou")
|
|
|
|
def better_unbeliever_completer(command):
|
|
|
|
"""Like unbeliever_completer but with less boilerplate"""
|
|
|
|
if command.arg_index == 1 and 'carcolh'.startswith(command.prefix):
|
|
|
|
return {'snail'}, len('lou ') + len(command.prefix)
|
|
|
|
|
|
|
|
To understand how xonsh uses completers and their return values try
|
|
|
|
to set :ref:`$XONSH_TRACE_COMPLETIONS <xonsh_trace_completions>` to ``True``:
|
|
|
|
|
|
|
|
.. code-block:: console
|
|
|
|
|
|
|
|
>>> $XONSH_TRACE_COMPLETIONS = True
|
|
|
|
>>> pip c<TAB>
|
|
|
|
TRACE COMPLETIONS: Getting completions with context:
|
|
|
|
CompletionContext(command=CommandContext(args=(CommandArg(value='pip', opening_quote='', closing_quote=''),), arg_index=1, prefix='c', suffix='', opening_quote='', closing_quote='', is_after_closing_quote=False, subcmd_opening=''), python=PythonContext('pip c', 5, is_sub_expression=False))
|
|
|
|
TRACE COMPLETIONS: Got 3 results from exclusive completer 'pip':
|
|
|
|
{RichCompletion('cache', append_space=True),
|
|
|
|
RichCompletion('check', append_space=True),
|
|
|
|
RichCompletion('config', append_space=True)}
|
|
|
|
|
2016-06-02 12:08:13 -04:00
|
|
|
|
|
|
|
|
|
|
|
Registering a Completer
|
|
|
|
=======================
|
|
|
|
|
|
|
|
Once you have created a completion function, you can add it to the list of
|
2020-12-17 00:56:22 +05:30
|
|
|
active completers via the ``completer add`` command or ``xonsh.completers.completer.add_one_completer`` function::
|
2016-06-02 12:08:13 -04:00
|
|
|
|
|
|
|
Usage:
|
2016-06-04 16:58:46 -04:00
|
|
|
completer add NAME FUNC [POS]
|
2016-06-02 12:08:13 -04:00
|
|
|
|
|
|
|
``NAME`` is a unique name to use in the listing
|
|
|
|
|
|
|
|
``FUNC`` is the name of a completer function to use.
|
|
|
|
|
|
|
|
``POS`` (optional) is a position into the list of completers at which the new completer should be added. It can be one of the following values:
|
|
|
|
|
2021-06-05 19:12:58 +03:00
|
|
|
* ``"start"`` indicates that the completer should be added to the start of the list of completers (
|
|
|
|
it should be run before all other exclusive completers)
|
2016-06-02 12:08:13 -04:00
|
|
|
* ``"end"`` indicates that the completer should be added to the end of the list of completers (it should be run after all others)
|
|
|
|
* ``">KEY"``, where ``KEY`` is a pre-existing name, indicates that this should be added after the completer named ``KEY``
|
|
|
|
* ``"<KEY"``, where ``KEY`` is a pre-existing name, indicates that this should be added before the completer named ``KEY``
|
|
|
|
|
2024-02-27 15:22:12 +00:00
|
|
|
If ``POS`` is not provided, it defaults to ``"end"``.
|
2016-06-04 16:58:46 -04:00
|
|
|
|
2018-09-30 13:59:08 -07:00
|
|
|
.. note:: It is also possible to manipulate ``__xonsh__.completers`` directly,
|
2016-06-02 12:08:13 -04:00
|
|
|
but this is the preferred method.
|
|
|
|
|
|
|
|
Removing a Completer
|
|
|
|
====================
|
|
|
|
|
|
|
|
To remove a completer from the list of active completers, run
|
2016-06-04 16:58:46 -04:00
|
|
|
``completer remove NAME``, where ``NAME`` is the unique identifier associated
|
2016-06-02 12:08:13 -04:00
|
|
|
with the completer you wish to remove.
|
2021-06-05 19:12:58 +03:00
|
|
|
|
|
|
|
Advanced Completions
|
|
|
|
====================
|
|
|
|
|
|
|
|
To provide further control over the completion, a completer can return a :class:`RichCompletion <xonsh.completers.tools.RichCompletion>` object.
|
|
|
|
Using this class, you can:
|
|
|
|
|
|
|
|
* Provide a specific prefix length per completion (via ``prefix_len``)
|
|
|
|
* Control how the completion looks in prompt-toolkit (via ``display``, ``description`` and ``style``) -
|
|
|
|
use the ``jedi`` xontrib to see it in action.
|
|
|
|
* Append a space after the completion (``append_space=True``)
|
|
|
|
|
|
|
|
|
|
|
|
Completing Closed String Literals
|
|
|
|
---------------------------------
|
|
|
|
When the cursor is appending to a closed string literal (i.e. cursor at the end of ``ls "/usr/"``), the following happens:
|
|
|
|
|
|
|
|
1. The closing quote will be appended to all completions.
|
|
|
|
I.e the completion ``/usr/bin`` will turn into ``/usr/bin"``.
|
|
|
|
To prevent this behavior, a completer can return a ``RichCompletion`` with ``append_closing_quote=False``.
|
|
|
|
2. If not specified, lprefix will cover the closing prefix.
|
|
|
|
I.e for ``ls "/usr/"``, the default lprefix will be 6 to include the closing quote.
|
|
|
|
To prevent this behavior, a completer can return a different lprefix or specify it inside ``RichCompletion``.
|
|
|
|
|
|
|
|
So if you want to change/remove the quotes from a string, the following completer can be written:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
@contextual_command_completer
|
|
|
|
def remove_quotes(command):
|
|
|
|
"""
|
|
|
|
Return a completer that will remove the quotes, i.e:
|
|
|
|
which "python"<TAB> -> which python
|
|
|
|
echo "hi<TAB> -> echo hi
|
|
|
|
ls "file with spaces"<TAB> -> ls file with spaces
|
|
|
|
"""
|
|
|
|
raw_prefix_len = len(command.raw_prefix) # this includes the closing quote if it exists
|
|
|
|
return {RichCompletion(command.prefix, prefix_len=raw_prefix_len, append_closing_quote=False)}
|
|
|
|
|
|
|
|
Legacy Completers Support
|
|
|
|
=========================
|
|
|
|
|
|
|
|
Before completion context was introduced, xonsh had a different readline-like completion API.
|
|
|
|
While this legacy API is not recommended, xonsh still supports it.
|
|
|
|
|
|
|
|
.. warning::
|
|
|
|
The legacy completers are less robust than the contextual system in many situations, for example:
|
|
|
|
|
|
|
|
* ``ls $(which<TAB>`` completes with the prefix ``$(which``
|
|
|
|
|
|
|
|
* ``ls 'a file<TAB>`` completes with the prefix ``file`` (instead of ``a file``)
|
|
|
|
|
|
|
|
See `Completion Context PR <https://github.com/xonsh/xonsh/pull/4017>`_ for more information.
|
|
|
|
|
|
|
|
Legacy completers are python functions that aren't marked by ``@contextual_completer`` and receive the following arguments:
|
|
|
|
|
|
|
|
* ``prefix``: the string to be matched (the last whitespace-separated token in the current line)
|
|
|
|
* ``line``: a string representing the entire current line
|
|
|
|
* ``begidx``: the index at which ``prefix`` starts in ``line``
|
|
|
|
* ``endidx``: the length of the ``prefix`` in ``line``
|
|
|
|
* ``ctx``: the current Python environment, as a dictionary mapping names to values
|
|
|
|
|
|
|
|
Their return value can be any of the variations of the contextual completers'.
|