Skip to content
Back to top

# Source code for xompletions.sudo

    """Completer for ``sudo``.

    ``sudo`` is more than a transparent command wrapper: it has its own flag
    grammar, accepts ``VAR=value`` environment assignments before the command,
    and honours the POSIX ``--`` end-of-options sentinel. The generic
    command-token skipper in :mod:`xonsh.completers.commands` only knows how to
    strip the literal ``sudo`` token, which leaves ``sudo -- foo``,
    ``sudo -u root foo`` and similar invocations without useful completions.

    This module walks ``ctx.args`` past every token that belongs to ``sudo``'s
    own prefix (flags, their values, ``VAR=value`` assignments, ``--``) and
    then either offers command names (when the cursor sits on the inner
    command word) or re-enters the full completion pipeline with the inner
    command at ``args[0]`` so the inner command's own completers
    (``man``, paths, ``pip``'s xompletion, …) surface.
    """

    import re

    from xonsh.built_ins import XSH
    from xonsh.completers.commands import complete_command
    from xonsh.parsers.completion_context import CommandContext, CompletionContext

    # Short-form sudo flags whose next argument is the flag's value
    # (``sudo -u root cmd``). Long-form variants are tracked separately below
    # because they can also appear as ``--user=root`` in a single token.
    _SUDO_OPTS_WITH_ARG = frozenset(
        {"-C", "-D", "-R", "-T", "-U", "-c", "-g", "-h", "-p", "-r", "-t", "-u"}
    )

    # Long-form sudo flags whose next argument is the flag's value when used
    # without an embedded ``=`` (``--user root`` vs ``--user=root``).
    _SUDO_LONG_OPTS_WITH_ARG = frozenset(
        {
            "--chdir",
            "--chroot",
            "--class",
            "--close-from",
            "--command-timeout",
            "--group",
            "--host",
            "--other-user",
            "--prompt",
            "--role",
            "--type",
            "--user",
        }
    )

    # ``VAR=value`` env-var assignments that sudo accepts before the command.
    _ENV_ASSIGN_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*=")

    def _find_inner_command_position(args) -> int:
        """Return the index in ``args`` where the inner command begins.

        Walks past ``sudo`` itself, every flag (consuming the next token when
        the flag is known to take a value), every ``VAR=value`` env-var
        assignment, and the ``--`` end-of-options sentinel. Stops at the first
        bare positional — that's the inner command. The return value can be
        greater than ``len(args)`` when a value-taking flag (``-u``,
        ``--user``) sits at the end without its value yet typed; callers
        interpret that as "the inner command hasn't been reached".
        """
        i = 1  # skip ``sudo`` itself
        while i < len(args):
            v = args[i].value
            if v == "--":
                return i + 1
            if v.startswith("-"):
                # ``--user=root`` carries its value inside the same token.
                if v.startswith("--") and "=" in v:
                    i += 1
                elif v in _SUDO_OPTS_WITH_ARG or v in _SUDO_LONG_OPTS_WITH_ARG:
                    # Flag consumes the next arg as its value.
                    i += 2
                else:
                    # Unknown / value-less flag (``-E``, ``-H``, …).
                    i += 1
                continue
            if _ENV_ASSIGN_RE.match(v):
                i += 1
                continue
            return i
        return i

    def xonsh_complete(ctx: CommandContext):
        """Complete arguments to ``sudo``."""
        inner = _find_inner_command_position(ctx.args)

        if ctx.arg_index < inner:
            # Cursor sits on the value-slot of a sudo flag (``sudo -u <Tab>``)
            # or some other token that still belongs to sudo's own prefix.
            # Defer to the rest of the completer pipeline.
            return None

        if ctx.arg_index == inner:
            # Cursor is on the inner-command slot. A ``-`` prefix here means
            # the user is still typing one of sudo's own flags (e.g.
            # ``sudo -E -<Tab>``) — defer to the man-page completer instead
            # of returning command names that start with ``-``.
            if ctx.prefix.startswith("-"):
                return None
            return complete_command(ctx)

        # Cursor is past the inner command's name, inside its own arguments.
        # Re-enter the completer pipeline with the inner command at args[0]
        # so its completers (paths, man flags, xompletions, …) fire.
        skipped = ctx._replace(
            args=ctx.args[inner:],
            arg_index=ctx.arg_index - inner,
        )
        completer = XSH.shell.shell.completer  # type: ignore[union-attr]
        return completer.complete_from_context(CompletionContext(skipped))