xonsh / xonsh

:shell: Python-powered shell. Full-featured and cross-platform.
http://xon.sh
Other
8.4k stars 638 forks source link

Document `bash_completion` limitations #3224

Open dotsdl opened 5 years ago

dotsdl commented 5 years ago

Reference #648

It is not clear what the limitations are for defining bash_completions. In particular the $BASH_COMPLETION_USER_DIR environment variable does not appear to be documented yet. Also, bash has idiosyncratic behavior in that it only allows a single directory for defining these completions.

For community

⬇️ Please click the 👍 reaction instead of leaving a +1 or 👍 comment

taconi commented 2 years ago

+1

taconi commented 2 years ago

Really this part of accepting only one file is not at all intuitive since the accepted type of the BASH_COMPLETIONS variable is a list or a tuple, something like the code below would only use the first file:

from pathlib import Path

$BASH_COMPLETIONS = [
    path.as_posix()
    for path in Path('/usr/share/bash-completion/completions').glob('*')
]

Maybe it's the behavior of the _get_bash_completions_source() function that handles the $BASH_COMPLETIONS variable with the following condition:

# paths is $BASH_COMPLETIONS
for path in map(pathlib.Path, paths):
    if path.is_file():
        return f'source "{path.as_posix()}"'

I would like to know if there is any other way to put multiple files or put a directory to complete.

taconi commented 2 years ago

To whom it may concern, this implementation worked for me:

```xsh from pathlib import Path from xonsh.completers.tools import contextual_command_completer_for from xonsh.parsers.completion_context import CommandContext def helper_completer(paths: Iterable[str], context: CommandContext): """ Reimplementation of the xonsh.completers.bash.complete_from_bash function, the only thing changed in this function was the `paths` variable which was $BASH_COMPLETIONS. """ from xonsh import platform from xonsh import tools from xonsh.built_ins import XSH from xonsh.completers.bash_completion import bash_completions from xonsh.completers.tools import RichCompletion env = XSH.env.detype() command = platform.bash_command() args = [arg.value for arg in context.args] prefix = context.prefix args.insert(context.arg_index, prefix) line = " ".join(args) # lengths of all args + joining spaces begidx = sum(len(a) for a in args[: context.arg_index]) + context.arg_index endidx = begidx + len(prefix) opening_quote = context.opening_quote closing_quote = context.closing_quote if closing_quote and not context.is_after_closing_quote: # there already are closing quotes after our cursor, don't complete # new ones (i.e. `ls "/pro"`) closing_quote = "" elif opening_quote and not closing_quote: # get the proper closing quote closing_quote = tools.RE_STRING_START.sub("", opening_quote) comps, lprefix = bash_completions( prefix, line, begidx, endidx, env=env, paths=paths, command=command, line_args=args, opening_quote=opening_quote, closing_quote=closing_quote, arg_index=context.arg_index, ) def enrich_comps(comp: str): append_space = False if comp.endswith(" "): append_space = True comp = comp.rstrip() # ``bash_completions`` may have added closing quotes: return RichCompletion( comp, append_closing_quote=False, append_space=append_space ) comps = set(map(enrich_comps, comps)) if lprefix == len(prefix): lprefix += len(context.opening_quote) if context.is_after_closing_quote: # since bash doesn't see the closing quote, we need to add its # length to lprefix lprefix += len(context.closing_quote) return comps, lprefix def add_completers(diretory: str): for path in Path(diretory).glob('*'): name = path.name _completer = contextual_command_completer_for(name)( partial(helper_completer, [path.as_posix()]) ) completer add f'{name}' _completer add_completers('/usr/share/bash-completion/completions') ```
anki-code commented 2 years ago

hi @taconi ! Thank you for the example! Do you know that you can make your xontrib for this using xontrib-template in 10 minutes? Wrap your code into the xontrib and share with xonsh users. Follow the xontrib promo guide. The example of the list of xontribs is xontrib Github topic.

taconi commented 2 years ago

Hi, @anki-code ! Thanks for the suggestion, I didn't know about this possibility, I'm new to the xonsh world. I created the xontrib bash-completions-dirs, in pypi there is an explanation of how to install and how to use.

Maybe this issue can be closed with this.

anki-code commented 2 years ago

@taconi it's awesome! Many thanks! Please add your xontrib to awesome-xontribs!

taconi commented 2 years ago

@taconi it's awesome! Many thanks! Please add your xontrib to awesome-xontribs!

PR was created.

anki-code commented 2 years ago

Thanks @taconi!

I'm new to the xonsh world.

It will be cool to start here - https://github.com/anki-code/xonsh-cheatsheet/blob/main/README.md

yolabingo commented 2 years ago

Thanks @taconi! I've been trying to get xonsh completions working with macports bash and had no luck until using this xontrib!

One additional constraint I found with macports is to set BASH_COMPLETION_USER_DIR to include BASH_COMPLETION_COMPAT_DIR, and to put all completions in that directory - perhaps this is specific to macports but I suspect this may be needed for homebrew as well.

Macports installs its bash_completion script at /opt/local/share/bash-completion/bash_completion. This script contains: compat_dir=${BASH_COMPLETION_COMPAT_DIR:-/opt/local/etc/bash_completion.d}

Completions in xonsh work if I either

  1. create /opt/local/etc/bash_completion.d/ and add/link completions files to it, or
  2. set BASH_COMPLETION_COMPAT_DIR in .bashrc to point my current bash completions scripts dir.

In either case, the directory with completions files must be set in .bashrc to BASH_COMPLETION_COMPAT_DIR and must also be included in xonsh $BASH_COMPLETIONS_DIRS

I also adhere to the script naming convention you mention on pypi

The completions file name must be the same as the command to be used in autocomplete

So with the following configs, completions are working in both bash and xonsh using xcontrib bash_completions_dirs and macports-installed bash: ~/.bashrc

...
export BASH_COMPLETION_COMPAT_DIR=/Users/yolabingo/.bash_completions
source /opt/local/share/bash-completion/bash_completion
source /Users/yolabingo/.bash_completions/git
source /Users/yolabingo/.bash_completions/kubectl
source /Users/yolabingo/.bash_completions/poetry
source /Users/yolabingo/.bash_completions/docker
...

~/.xonshrc

...
# macports' bash_completion file:
$BASH_COMPLETIONS = ['/opt/local/share/bash-completion/bash_completion']
$BASH_COMPLETIONS_DIRS = ['/Users/yolabingo/.bash_completions']
xontrib load  bash_completions_dirs ...