From 315e448369e0abf82912b5837c2c1750729ef7ef Mon Sep 17 00:00:00 2001 From: Kenneth Belitzky Date: Fri, 15 Aug 2025 23:30:11 +0000 Subject: [PATCH 1/7] feat(generate): enhance template variable parsing and merging logic --- struct_module/commands/generate.py | 48 ++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/struct_module/commands/generate.py b/struct_module/commands/generate.py index 3d9f1f1..7138745 100644 --- a/struct_module/commands/generate.py +++ b/struct_module/commands/generate.py @@ -30,6 +30,33 @@ def __init__(self, parser): choices=['console', 'file'], default='file', help='Output mode') parser.set_defaults(func=self.execute) + def _parse_template_vars(self, vars_str): + """Parse a comma-separated KEY=VALUE string into a dict safely. + - Ignores empty tokens and trailing commas + - Supports values containing '=' by splitting only on the first '=' + - Logs and skips malformed entries without raising + """ + result = {} + if not vars_str: + return result + # Normalize by removing accidental leading/trailing commas and whitespace + tokens = [t.strip() for t in vars_str.strip(', ').split(',')] + for token in tokens: + if not token: + continue + if '=' not in token: + # Skip malformed item but warn + self.logger.warning(f"Skipping malformed template var (no '='): '{token}'") + continue + key, value = token.split('=', 1) + key = key.strip() + value = value + if not key: + self.logger.warning(f"Skipping template var with empty key: '{token}'") + continue + result[key] = value + return result + def _deep_merge_dicts(self, dict1, dict2): """ Deep merge two dictionaries, with dict2 values overriding dict1 values. @@ -146,7 +173,8 @@ def _create_structure(self, args, mappings=None, summary=None, print_summary=Tru if config is None: return summary if summary is not None else None - template_vars = dict(item.split('=') for item in args.vars.split(',')) if args.vars else None + # Safely parse template variables + template_vars = self._parse_template_vars(args.vars) if getattr(args, 'vars', None) else {} config_structure = config.get('files', config.get('structure', [])) config_folders = config.get('folders', []) config_variables = config.get('variables', []) @@ -301,8 +329,18 @@ def _create_structure(self, args, mappings=None, summary=None, print_summary=Tru merged_vars = ",".join( [f"{k}={v}" for k, v in rendered_with.items()]) - if args.vars: - merged_vars = args.vars + "," + merged_vars + # Merge parent args.vars safely without introducing trailing commas + if getattr(args, 'vars', None): + parts = [] + parent_vars = args.vars.strip().strip(',') + if parent_vars: + parts.append(parent_vars) + if merged_vars: + parts.append(merged_vars) + merged_vars = ",".join(parts) + + # If nothing to merge, keep None to avoid accidental truthiness with empty string + merged_vars = merged_vars if merged_vars else None if isinstance(content['struct'], str): self._create_structure({ @@ -345,8 +383,8 @@ def _create_structure(self, args, mappings=None, summary=None, print_summary=Tru self.logger.info(f" ✅ Created: {summary['created']}") self.logger.info(f" ✅ Updated: {summary['updated']}") self.logger.info(f" 📝 Appended: {summary['appended']}") - self.logger.info(f" ⏭️ Skipped: {summary['skipped']}") - self.logger.info(f" 🗄️ Backed up: {summary['backed_up']}") + self.logger.info(f" ⏭️ Skipped: {summary['skipped']}") + self.logger.info(f" 🗄️ Backed up: {summary['backed_up']}") self.logger.info(f" 🔁 Renamed: {summary['renamed']}") self.logger.info(f" 📁 Folders created: {summary['folders']}") if args.dry_run: From 36b8bf5bdc627f2eb3657b5fd9664096468387e4 Mon Sep 17 00:00:00 2001 From: Kenneth Belitzky Date: Fri, 15 Aug 2025 20:52:27 -0300 Subject: [PATCH 2/7] feat(completions): switch to shtab static completions\n\n- Expose get_parser() for shtab\n- Add --print-completion via shtab when available\n- Rework Detected shell: zsh # Install shtab (once, in your environment): python -m pip install shtab # Generate static zsh completion for 'struct': mkdir -p ~/.zfunc python -m shtab struct_module.main:get_parser -s zsh -o ~/.zfunc/_struct # Ensure zsh loads user functions/completions (append to ~/.zshrc if needed): echo "fpath=(~/.zfunc $fpath)" >> ~/.zshrc echo "autoload -U compinit && compinit" >> ~/.zshrc # Apply now (or open a new shell): exec zsh Tip: You can also print completion directly via: struct --print-completion to generate static files for zsh/bash/fish\n- Replace argcomplete with shtab in requirements.txt --- requirements.txt | 2 +- struct_module/commands/completion.py | 39 ++++++++++++++++------------ struct_module/main.py | 29 ++++++++++++++------- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/requirements.txt b/requirements.txt index 35527b5..1b1be23 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ openai python-dotenv jinja2 PyGithub -argcomplete +shtab colorlog boto3 google-cloud diff --git a/struct_module/commands/completion.py b/struct_module/commands/completion.py index cb7053b..08ffa07 100644 --- a/struct_module/commands/completion.py +++ b/struct_module/commands/completion.py @@ -6,7 +6,7 @@ class CompletionCommand(Command): def __init__(self, parser): super().__init__(parser) - parser.description = "Manage CLI shell completions for struct (argcomplete)" + parser.description = "Manage CLI shell completions for struct (shtab-generated)" sub = parser.add_subparsers(dest="action") install = sub.add_parser("install", help="Print the commands to enable completion for your shell") @@ -29,27 +29,32 @@ def _install(self, args): print(f"Detected shell: {shell}") if shell == "bash": - print("\n# One-time dependency (if not installed):") - print("python -m pip install argcomplete") - print("\n# Enable completion for 'struct' in bash (append to ~/.bashrc):") - print('echo "eval \"$(register-python-argcomplete struct)\"" >> ~/.bashrc') - print("\n# Apply now:") + print("\n# Install shtab (once, in your environment):") + print("python -m pip install shtab") + print("\n# Generate static bash completion for 'struct':") + print("mkdir -p ~/.local/share/bash-completion/completions") + print("python -m shtab struct_module.main:get_parser -s bash -o ~/.local/share/bash-completion/completions/struct") + print("\n# Apply now (or open a new shell):") print("source ~/.bashrc") elif shell == "zsh": - print("\n# One-time dependency (if not installed):") - print("python -m pip install argcomplete") - print("\n# Enable completion for 'struct' in zsh (append to ~/.zshrc):") - print('echo "eval \"$(register-python-argcomplete --shell zsh struct)\"" >> ~/.zshrc') - print("\n# Apply now:") - print("source ~/.zshrc") + print("\n# Install shtab (once, in your environment):") + print("python -m pip install shtab") + print("\n# Generate static zsh completion for 'struct':") + print("mkdir -p ~/.zfunc") + print("python -m shtab struct_module.main:get_parser -s zsh -o ~/.zfunc/_struct") + print("\n# Ensure zsh loads user functions/completions (append to ~/.zshrc if needed):") + print('echo "fpath=(~/.zfunc $fpath)" >> ~/.zshrc') + print('echo "autoload -U compinit && compinit" >> ~/.zshrc') + print("\n# Apply now (or open a new shell):") + print("exec zsh") elif shell == "fish": - print("\n# One-time dependency (if not installed):") - print("python -m pip install argcomplete") - print("\n# Install fish completion file for 'struct':") + print("\n# Install shtab (once, in your environment):") + print("python -m pip install shtab") + print("\n# Generate static fish completion for 'struct':") print('mkdir -p ~/.config/fish/completions') - print('register-python-argcomplete --shell fish struct > ~/.config/fish/completions/struct.fish') + print('python -m shtab struct_module.main:get_parser -s fish -o ~/.config/fish/completions/struct.fish') print("\n# Apply now:") print("fish -c 'source ~/.config/fish/completions/struct.fish'") @@ -57,4 +62,4 @@ def _install(self, args): self.logger.error(f"Unsupported shell: {shell}. Supported: {', '.join(SUPPORTED_SHELLS)}") return - print("\nTip: If 'register-python-argcomplete' is not found, try:\n python -m argcomplete.shellintegration ") + print("\nTip: You can also print completion directly via: struct --print-completion ") diff --git a/struct_module/main.py b/struct_module/main.py index 5161542..d2fe2d2 100644 --- a/struct_module/main.py +++ b/struct_module/main.py @@ -1,4 +1,4 @@ -import argparse, argcomplete +import argparse import logging from dotenv import load_dotenv from struct_module.utils import read_config_file, merge_configs @@ -10,11 +10,15 @@ from struct_module.commands.mcp import MCPCommand from struct_module.logging_config import configure_logging - +# Optional dependency: shtab for static shell completion generation +try: + import shtab # type: ignore +except Exception: # pragma: no cover - optional at runtime + shtab = None load_dotenv() -def main(): +def get_parser(): parser = argparse.ArgumentParser( description="Generate project structure from YAML configuration.", prog="struct", @@ -35,11 +39,19 @@ def main(): from struct_module.commands.init import InitCommand InitCommand(subparsers.add_parser('init', help='Initialize a basic .struct.yaml in the target directory')) - # completion installer + # completion manager from struct_module.commands.completion import CompletionCommand CompletionCommand(subparsers.add_parser('completion', help='Manage shell completions')) - argcomplete.autocomplete(parser) + # Add shtab completion printing flags if available + if shtab is not None: + # Adds --print-completion and --shell flags + shtab.add_argument_to(parser) + + return parser + +def main(): + parser = get_parser() args = parser.parse_args() @@ -49,14 +61,13 @@ def main(): parser.exit() # Read config file if provided - if args.config_file: + if getattr(args, 'config_file', None): file_config = read_config_file(args.config_file) args = argparse.Namespace(**merge_configs(file_config, args)) - logging_level = getattr(logging, args.log.upper(), logging.INFO) - - configure_logging(level=logging_level, log_file=args.log_file) + logging_level = getattr(logging, getattr(args, 'log', 'INFO').upper(), logging.INFO) + configure_logging(level=logging_level, log_file=getattr(args, 'log_file', None)) args.func(args) From 17e81bc76f5afa2f67b69cec05f58436f8d7948b Mon Sep 17 00:00:00 2001 From: Kenneth Belitzky Date: Fri, 15 Aug 2025 21:01:51 -0300 Subject: [PATCH 3/7] docs(completion): update docs to use shtab static completion instructions and adjust references --- docs/cli-reference.md | 2 +- docs/completion.md | 144 +++++++++++++----------------------------- docs/installation.md | 6 +- 3 files changed, 46 insertions(+), 106 deletions(-) diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 79ada10..af91bd1 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -117,7 +117,7 @@ Usage: struct completion install [bash|zsh|fish] ``` -- If no shell is provided, the command attempts to auto-detect your current shell and prints the exact commands to enable argcomplete-based completion for struct. +- If no shell is provided, the command attempts to auto-detect your current shell and prints the exact commands to generate and install static completion files via shtab. - This does not modify your shell configuration; it only prints the commands you can copy-paste. ### `init` diff --git a/docs/completion.md b/docs/completion.md index a8cb7f8..e29636d 100644 --- a/docs/completion.md +++ b/docs/completion.md @@ -1,13 +1,13 @@ # Command-Line Auto-Completion -STRUCT provides intelligent auto-completion for commands, options, and **structure names** using [argcomplete](https://kislyuk.github.io/argcomplete/). This makes discovering and using available structures much faster and more user-friendly. +STRUCT provides intelligent auto-completion for commands, options, and structure names using static completion scripts generated by [shtab](https://github.com/Iterative/shtab). This approach is reliable across shells and doesn’t require runtime hooks or markers. -!!! tip "New Feature: Structure Name Completion" - STRUCT now automatically completes structure names when using `struct generate`, showing all 47+ available structures from both built-in and custom paths! +!!! tip "Structure Name Completion" + STRUCT completes structure names when using `struct generate`, showing available structures from both built-in and custom paths. ## Quick Setup -The easiest way is to ask struct to print the exact commands for your shell: +Ask struct to print the exact commands for your shell: ```sh # Auto-detect current shell and print install steps @@ -19,77 +19,45 @@ struct completion install bash struct completion install fish ``` -You can still follow the manual steps below if you prefer. +You can also generate completion files manually with shtab as shown below. -For most users, this simple setup will enable full completion: +## Manual Installation -```sh -# Install (if not already installed) -pip install argcomplete - -# Enable completion for current session -eval "$(register-python-argcomplete struct)" - -# Make permanent - add to your ~/.zshrc or ~/.bashrc -echo 'eval "$(register-python-argcomplete struct)"' >> ~/.zshrc -``` - -## Detailed Installation - -### 1. Install argcomplete - -```sh -pip install argcomplete -``` - -### 2. Enable Global Completion (Optional) - -This step is optional but can be done once per system: - -```sh -activate-global-python-argcomplete -``` - -This command sets up global completion for all Python scripts that use argcomplete. - -### 3. Register the Script - -Add the following line to your shell's configuration file: - -**For Bash** (`.bashrc` or `.bash_profile`): - -```sh -eval "$(register-python-argcomplete struct)" -``` - -**For Zsh** (`.zshrc`): +### 1) Install shtab ```sh -eval "$(register-python-argcomplete struct)" +pip install shtab ``` -**For Fish** (`.config/fish/config.fish`): +### 2) Generate and install completion for your shell -```fish -register-python-argcomplete --shell fish struct | source -``` - -### 4. Reload Your Shell - -```sh -# For Bash -source ~/.bashrc +- Zsh + ```sh + mkdir -p ~/.zfunc + python -m shtab struct_module.main:get_parser -s zsh -o ~/.zfunc/_struct + # ensure in ~/.zshrc + fpath=(~/.zfunc $fpath) + autoload -U compinit && compinit + exec zsh + ``` -# For Zsh -source ~/.zshrc +- Bash + ```sh + mkdir -p ~/.local/share/bash-completion/completions + python -m shtab struct_module.main:get_parser -s bash -o ~/.local/share/bash-completion/completions/struct + source ~/.bashrc + ``` -# For Fish -source ~/.config/fish/config.fish -``` +- Fish + ```sh + mkdir -p ~/.config/fish/completions + python -m shtab struct_module.main:get_parser -s fish -o ~/.config/fish/completions/struct.fish + fish -c 'source ~/.config/fish/completions/struct.fish' + ``` ## Usage -After completing the setup, you can use auto-completion by typing part of a command and pressing `Tab`: +After installing the completion, use Tab to complete commands/options: ### Command Completion ```sh @@ -132,12 +100,7 @@ struct generate --log ### Per-Project Completion -If you only want completion for specific projects, you can add completion to your project's virtual environment activation script: - -```sh -# In your .venv/bin/activate file, add: -eval "$(register-python-argcomplete struct)" -``` +If you only want completion for a specific project/venv, generate the completion from the project’s venv and place it under your user completion directory (examples above). No runtime eval is needed. ### Custom Completion @@ -158,60 +121,41 @@ complete -F _struct_structures struct-generate ### Completion Not Working -1. **Check argcomplete installation**: - - ```sh - python -c "import argcomplete; print('OK')" - ``` - -2. **Verify global activation**: +1. Verify shtab is installed in the environment you’re using: ```sh - activate-global-python-argcomplete --user + python -c "import shtab; print('OK')" ``` -3. **Check shell configuration**: - Make sure the eval statement is in the correct shell configuration file. - -4. **Restart your shell**: - Sometimes you need to completely restart your terminal. - -### Slow Completion +2. Confirm the completion file exists in the expected location and is readable. -If completion is slow, you can enable caching: - -```sh -export ARGCOMPLETE_USE_TEMPFILES=1 -``` +3. Ensure your shell is configured to load completions: + - zsh: fpath includes ~/.zfunc and compinit is run. + - bash: bash-completion is installed and sourced (on some distros). + - fish: the file is in ~/.config/fish/completions/. -Add this to your shell configuration file for persistent caching. +4. Restart your shell (or run `exec zsh`/`source ~/.bashrc`). ### Debug Completion -Enable debug mode to troubleshoot completion issues: - -```sh -export _ARGCOMPLETE_DEBUG=1 -struct -``` +For shell-specific debugging, check that the generated file contains the struct completion function and is in the correct directory for your shell. ## Platform-Specific Notes ### macOS -On macOS, you might need to install bash-completion first: +On macOS, you may need to install bash-completion (for bash) or ensure zsh’s compinit is configured: ```sh # Using Homebrew brew install bash-completion - -# Then add to ~/.bash_profile: +# bash profile [[ -r "/usr/local/etc/profile.d/bash_completion.sh" ]] && . "/usr/local/etc/profile.d/bash_completion.sh" ``` ### Windows -For Windows users using Git Bash or WSL, follow the same steps as Linux. For PowerShell, argcomplete support is limited. +For Windows users using Git Bash or WSL, follow the same steps as Linux. PowerShell is not covered by shtab; use bash/zsh/fish. ### Docker diff --git a/docs/installation.md b/docs/installation.md index 8de341d..e822d8c 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -9,11 +9,7 @@ pip install git+https://github.com/httpdss/struct.git ``` !!! tip "Enable Auto-Completion" - After installation, enable command-line auto-completion for better productivity: - ```sh - eval "$(register-python-argcomplete struct)" - ``` - For permanent setup, see the [Command-Line Completion](completion.md) guide. + After installation, enable command-line auto-completion using static scripts generated by shtab. See the [Command-Line Completion](completion.md) guide for per-shell instructions. ## From Source From 7ef57bd4ae70fd0a78cbafbf2d00dce4c8c755a2 Mon Sep 17 00:00:00 2001 From: Kenneth Belitzky Date: Fri, 15 Aug 2025 21:17:19 -0300 Subject: [PATCH 4/7] feat(completion): add ansible-playbook chef-cookbook ci-cd-pipelines cloudformation-files configs/codeowners configs/devcontainer configs/editor-config configs/eslint configs/jshint configs/kubectl configs/prettier docker-files documentation-template git-hooks github/chatmodes/plan github/instructions/generic github/prompts/generic github/prompts/react-form github/prompts/security-api github/prompts/struct github/templates github/workflows/codeql github/workflows/execute-tf-workflow github/workflows/labeler github/workflows/pre-commit github/workflows/release-drafter github/workflows/run-struct github/workflows/stale helm-chart kubernetes-manifests project/custom-structures project/generic project/go project/java project/n8n project/nodejs project/python project/ruby project/rust prompts/run-struct-trigger terraform/apps/aws-accounts terraform/apps/environments terraform/apps/generic terraform/apps/github-organization terraform/apps/init terraform/modules/generic vagrant-files and dynamic zsh completion for structure names --- struct_module/commands/list.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/struct_module/commands/list.py b/struct_module/commands/list.py index 3a60bbf..1e35399 100644 --- a/struct_module/commands/list.py +++ b/struct_module/commands/list.py @@ -10,6 +10,7 @@ class ListCommand(Command): def __init__(self, parser): super().__init__(parser) parser.add_argument('-s', '--structures-path', type=str, help='Path to structure definitions') + parser.add_argument('--names-only', action='store_true', help='Print only structure names, one per line (for shell completion)') parser.add_argument('--mcp', action='store_true', help='Enable MCP (Model Context Protocol) integration') parser.set_defaults(func=self.execute) @@ -30,7 +31,6 @@ def _list_structures(self, args): else: paths_to_list = [contribs_path] - print("📃 Listing available structures\n") all_structures = set() for path in paths_to_list: for root, _, files in os.walk(path): @@ -39,11 +39,23 @@ def _list_structures(self, args): rel_path = os.path.relpath(file_path, path) if file.endswith(".yaml"): rel_path = rel_path[:-5] - if path != contribs_path: + # Mark custom path entries with '+ ' unless names-only requested + if not args.names_only and path != contribs_path: rel_path = f"+ {rel_path}" all_structures.add(rel_path) sorted_list = sorted(all_structures) + + if args.names_only: + # Print plain names without bullets or headers, remove '+ ' marker + for structure in sorted_list: + if structure.startswith('+ '): + print(structure[2:]) + else: + print(structure) + return + + print("📃 Listing available structures\n") for structure in sorted_list: print(f" - {structure}") From 92c53671bd6e8c9a0538e610e237e098e1dedc1a Mon Sep 17 00:00:00 2001 From: Kenneth Belitzky Date: Sat, 16 Aug 2025 01:54:13 +0000 Subject: [PATCH 5/7] feat(commands): add descriptions to command parsers for better clarity --- struct_module/commands/generate.py | 1 + struct_module/commands/generate_schema.py | 1 + struct_module/commands/info.py | 1 + struct_module/commands/list.py | 1 + struct_module/commands/mcp.py | 1 + struct_module/commands/validate.py | 1 + 6 files changed, 6 insertions(+) diff --git a/struct_module/commands/generate.py b/struct_module/commands/generate.py index 7138745..2a078d3 100644 --- a/struct_module/commands/generate.py +++ b/struct_module/commands/generate.py @@ -12,6 +12,7 @@ class GenerateCommand(Command): def __init__(self, parser): super().__init__(parser) + parser.description = "Generate the project structure from a YAML configuration file" structure_arg = parser.add_argument('structure_definition', type=str, help='Path to the YAML configuration file') structure_arg.completer = structures_completer parser.add_argument('base_path', type=str, help='Base path where the structure will be created') diff --git a/struct_module/commands/generate_schema.py b/struct_module/commands/generate_schema.py index 3e3fb4e..f04421a 100644 --- a/struct_module/commands/generate_schema.py +++ b/struct_module/commands/generate_schema.py @@ -6,6 +6,7 @@ class GenerateSchemaCommand(Command): def __init__(self, parser): super().__init__(parser) + parser.description = "Generate JSON schema for available structures" parser.add_argument('-s', '--structures-path', type=str, help='Path to structure definitions') parser.add_argument('-o', '--output', type=str, help='Output file path for the schema (default: stdout)') parser.set_defaults(func=self.execute) diff --git a/struct_module/commands/info.py b/struct_module/commands/info.py index 8a6569a..9b36777 100644 --- a/struct_module/commands/info.py +++ b/struct_module/commands/info.py @@ -8,6 +8,7 @@ class InfoCommand(Command): def __init__(self, parser): super().__init__(parser) + parser.description = "Show information about the package or structure definition" parser.add_argument('structure_definition', type=str, help='Name of the structure definition') parser.add_argument('-s', '--structures-path', type=str, help='Path to structure definitions') parser.add_argument('--mcp', action='store_true', help='Enable MCP (Model Context Protocol) integration') diff --git a/struct_module/commands/list.py b/struct_module/commands/list.py index 1e35399..e55ebc7 100644 --- a/struct_module/commands/list.py +++ b/struct_module/commands/list.py @@ -9,6 +9,7 @@ class ListCommand(Command): def __init__(self, parser): super().__init__(parser) + parser.description = "List available structures" parser.add_argument('-s', '--structures-path', type=str, help='Path to structure definitions') parser.add_argument('--names-only', action='store_true', help='Print only structure names, one per line (for shell completion)') parser.add_argument('--mcp', action='store_true', help='Enable MCP (Model Context Protocol) integration') diff --git a/struct_module/commands/mcp.py b/struct_module/commands/mcp.py index a44eeac..b194718 100644 --- a/struct_module/commands/mcp.py +++ b/struct_module/commands/mcp.py @@ -8,6 +8,7 @@ class MCPCommand(Command): def __init__(self, parser): super().__init__(parser) + parser.description = "MCP (Model Context Protocol) support for struct tool" parser.add_argument('--server', action='store_true', help='Start the MCP server for stdio communication') parser.set_defaults(func=self.execute) diff --git a/struct_module/commands/validate.py b/struct_module/commands/validate.py index 15f7e38..4c3361a 100644 --- a/struct_module/commands/validate.py +++ b/struct_module/commands/validate.py @@ -9,6 +9,7 @@ class ValidateCommand(Command): def __init__(self, parser): super().__init__(parser) + parser.description = "Validate a YAML configuration file for structure definitions" parser.add_argument('yaml_file', type=str, help='Path to the YAML configuration file') parser.set_defaults(func=self.execute) From ac0cc0eec77260d639e0635d9ee73b26c6c3d734 Mon Sep 17 00:00:00 2001 From: Kenneth Belitzky Date: Sat, 16 Aug 2025 02:22:56 +0000 Subject: [PATCH 6/7] feat(completion): update installation instructions to use --print-completion for generating shell completions --- docs/completion.md | 9 ++++++--- struct_module/commands/completion.py | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/completion.md b/docs/completion.md index e29636d..d53814c 100644 --- a/docs/completion.md +++ b/docs/completion.md @@ -32,9 +32,10 @@ pip install shtab ### 2) Generate and install completion for your shell - Zsh + ```sh mkdir -p ~/.zfunc - python -m shtab struct_module.main:get_parser -s zsh -o ~/.zfunc/_struct + struct --print-completion zsh > ~/.zfunc/_struct # ensure in ~/.zshrc fpath=(~/.zfunc $fpath) autoload -U compinit && compinit @@ -42,16 +43,18 @@ pip install shtab ``` - Bash + ```sh mkdir -p ~/.local/share/bash-completion/completions - python -m shtab struct_module.main:get_parser -s bash -o ~/.local/share/bash-completion/completions/struct + struct --print-completion bash > ~/.local/share/bash-completion/completions/struct source ~/.bashrc ``` - Fish + ```sh mkdir -p ~/.config/fish/completions - python -m shtab struct_module.main:get_parser -s fish -o ~/.config/fish/completions/struct.fish + struct --print-completion fish > ~/.config/fish/completions/struct.fish fish -c 'source ~/.config/fish/completions/struct.fish' ``` diff --git a/struct_module/commands/completion.py b/struct_module/commands/completion.py index 08ffa07..43efbb6 100644 --- a/struct_module/commands/completion.py +++ b/struct_module/commands/completion.py @@ -33,7 +33,7 @@ def _install(self, args): print("python -m pip install shtab") print("\n# Generate static bash completion for 'struct':") print("mkdir -p ~/.local/share/bash-completion/completions") - print("python -m shtab struct_module.main:get_parser -s bash -o ~/.local/share/bash-completion/completions/struct") + print("struct --print-completion bash > ~/.local/share/bash-completion/completions/struct") print("\n# Apply now (or open a new shell):") print("source ~/.bashrc") @@ -42,7 +42,7 @@ def _install(self, args): print("python -m pip install shtab") print("\n# Generate static zsh completion for 'struct':") print("mkdir -p ~/.zfunc") - print("python -m shtab struct_module.main:get_parser -s zsh -o ~/.zfunc/_struct") + print("struct --print-completion zsh > ~/.zfunc/_struct") print("\n# Ensure zsh loads user functions/completions (append to ~/.zshrc if needed):") print('echo "fpath=(~/.zfunc $fpath)" >> ~/.zshrc') print('echo "autoload -U compinit && compinit" >> ~/.zshrc') @@ -54,7 +54,7 @@ def _install(self, args): print("python -m pip install shtab") print("\n# Generate static fish completion for 'struct':") print('mkdir -p ~/.config/fish/completions') - print('python -m shtab struct_module.main:get_parser -s fish -o ~/.config/fish/completions/struct.fish') + print('struct --print-completion fish > ~/.config/fish/completions/struct.fish') print("\n# Apply now:") print("fish -c 'source ~/.config/fish/completions/struct.fish'") From cc02dee0c51d1ad42525f2d02a055431ed0dcae9 Mon Sep 17 00:00:00 2001 From: Kenneth Belitzky Date: Sat, 16 Aug 2025 15:51:09 -0300 Subject: [PATCH 7/7] test(completion): update installation instructions to shtab --print-completion for bash/zsh/fish --- tests/test_completion_command.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_completion_command.py b/tests/test_completion_command.py index 43a68a0..d7d9692 100644 --- a/tests/test_completion_command.py +++ b/tests/test_completion_command.py @@ -21,8 +21,8 @@ def test_completion_install_bash_explicit(): cmd._install(args) out = _gather_print_output(mock_print) assert "Detected shell: bash" in out - assert "register-python-argcomplete struct" in out - assert "~/.bashrc" in out + assert "struct --print-completion bash" in out + assert "~/.local/share/bash-completion/completions/struct" in out def test_completion_install_zsh_explicit(): @@ -33,8 +33,8 @@ def test_completion_install_zsh_explicit(): cmd._install(args) out = _gather_print_output(mock_print) assert "Detected shell: zsh" in out - assert "register-python-argcomplete --shell zsh struct" in out - assert "~/.zshrc" in out + assert "struct --print-completion zsh" in out + assert "~/.zfunc/_struct" in out def test_completion_install_fish_explicit(): @@ -45,7 +45,7 @@ def test_completion_install_fish_explicit(): cmd._install(args) out = _gather_print_output(mock_print) assert "Detected shell: fish" in out - assert "register-python-argcomplete --shell fish struct" in out + assert "struct --print-completion fish" in out assert "~/.config/fish/completions/struct.fish" in out @@ -58,4 +58,4 @@ def test_completion_install_auto_detect_zsh(): cmd._install(args) out = _gather_print_output(mock_print) assert "Detected shell: zsh" in out - assert "register-python-argcomplete --shell zsh struct" in out + assert "struct --print-completion zsh" in out