Feature/gh73 implement bash completion#74
Conversation
- Implemented tab completion (bash only) for the xts CLI - Added tab completion to the install command
|
I have read the CLA Document and I hereby sign the CLA 1 out of 2 committers have signed the CLA. |
There was a problem hiding this comment.
Pull request overview
This PR adds bash tab-completion support to the xts CLI and updates installation to deploy the completion script automatically for bash users, alongside some CLI parsing/help-formatting changes to better surface aliases and subcommands.
Changes:
- Added bash completion plumbing (
_XTS_COMPLETE) and completion installation viaxts-install. - Introduced a custom Rich help formatter to render aliases and subcommands in separate help sections.
- Updated argparse wiring (including alias management parsing) and packaging metadata to ship the completion script.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| src/xts_core/xts.py | Adds completion entrypoint logic and refactors top-level parsing to model aliases/subcommands. |
| src/xts_core/xts_rich_help_formatter.py | New formatter to split help output into “Subcommands” vs “Aliases”. |
| src/xts_core/xts_arg_parser.py | Allows passing a formatter class and uses raise SystemExit in error(). |
| src/xts_core/xts_alias.py | Refactors alias CLI parsing into a subcommand-style parser setup. |
| src/xts_core/plugins/xts_allocator_client.py | Replaces sys.exit(1) with raise SystemExit(1) for consistency. |
| src/xts_core/install.py | Installs bash completion script and appends PATH/source lines to bash rc files. |
| src/xts_core/data/xts_bash_completion.sh | New bash completion script that invokes xts in completion mode. |
| pyproject.toml | Updates dependencies and config to include shipped completion script. |
| .github/instructions/*.md, .github/copilot-instructions.md | Adds repository contribution/instruction documents (non-runtime). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| except Exception as e: | ||
| error( | ||
| 'An unrecognised command caused an error\n\n' | ||
| f'Command Args: [{" ".join(args)}]\n\n' | ||
| f'{str(e)}' | ||
| ) | ||
| error( | ||
| 'An unrecognised command caused an error\n\n' | ||
| f'Command Args: [{" ".join(arguments)}]\n\n' | ||
| f'{str(e)}') |
| os.environ['_ARGPARSE_COMPLETE'] = os.getenv('_XTS_COMPLETE') | ||
| completion = argparse_completion.get_completion(arg_parser) | ||
| if 'alias_name' in completion: | ||
| completion.remove('alias_name') | ||
| if len(completion) == 0 and (comp_words:=(os.getenv('COMP_WORDS').split()))[1] in xts_alias.load_aliases().keys(): | ||
| os.environ['_YAML_RUNNER_COMPLETE'] = os.getenv('_XTS_COMPLETE') | ||
| alias = comp_words[1] | ||
| comp_words.remove('xts') | ||
| comp_words.remove(alias) | ||
| os.environ['COMP_WORDS'] = " ".join(comp_words) | ||
| self._run_yaml_runner(alias, comp_words) | ||
| else: | ||
| print('\n'.join(completion)) |
| case 'alias': | ||
| alias_name_subparser = list(filter(lambda x: x.dest == 'alias_name',parser._actions))[0] | ||
| alias_subparser = alias_name_subparser.choices.get('alias') | ||
| xts_alias.run_alias_builtin(alias_subparser) |
| @@ -174,63 +176,85 @@ def _parse_first_arg(self): | |||
| list[str]: Remaining args starting with the command name, e.g. ["run", ...]. | |||
| choice_action = list(filter(lambda x: parser.prog == f'xts {x.dest}', action._choices_actions))[0] | ||
| choice_tuple = (choice, choice_action) |
| def _install_bash(user_home: Path): | ||
| if (bashrc:=user_home.joinpath(Path('.bashrc'))).exists(): | ||
| with open(user_home.joinpath(Path('.bashrc')), 'a') as f: | ||
| f.write('\n# XTS PATH\n') | ||
| f.write(f'export PATH="{user_home.joinpath(Path(".xts/bin"))}:$PATH"\n') | ||
| _install_bash_completion(bashrc) | ||
| elif (bash_profile:=user_home.joinpath(Path('.bash_profile'))).exists(): | ||
| with open(user_home.joinpath(Path('.bash_profile')), 'a') as f: | ||
| f.write('\n# XTS PATH\n') | ||
| f.write(f'export PATH="{user_home.joinpath(Path(".xts/bin"))}:$PATH"\n') | ||
| _install_bash_completion(bash_profile) | ||
| else: | ||
| warning('Could not find .bashrc or .bash_profile to add xts to PATH. Please add the following line to your shell config file:\n' + | ||
| f'export PATH="{user_home.joinpath(Path(".xts/bin"))}:$PATH"\n') | ||
|
|
| with open(rc_file, 'a') as f: | ||
| f.write('\n# XTS bash completion\n') | ||
| f.write(f'source {bash_completion_dir}/xts_bash_completion.sh\n') |
| # This ensures data/ is included in sdist/wheel | ||
| [tool.setuptools.data-files] | ||
| # Installs data into a shared location in site-packages | ||
| "xts_core_data" = ["data/**/*"] |
|
|
||
|
|
||
| def _parse_first_arg(self): | ||
| def _setup_first_parser(self): |
| alias_parser = first_arg_subparsers.add_parser('alias', | ||
| help='Manage aliases (add, list, remove)') | ||
| xts_alias.setup_alias_parser(alias_parser) |
closes #73