Skip to content

improvements and fixes to the tcsh completion logic#213

Draft
ThomasWaldmann wants to merge 1 commit intoiterative:mainfrom
ThomasWaldmann:fix-tcsh
Draft

improvements and fixes to the tcsh completion logic#213
ThomasWaldmann wants to merge 1 commit intoiterative:mainfrom
ThomasWaldmann:fix-tcsh

Conversation

@ThomasWaldmann
Copy link
Copy Markdown
Contributor

Issues:

  1. Subcommand Positional Completion: shtab lacks native support for auto-completing positional arguments that belong to subcommands in tcsh (e.g., borg help <topic>). This builds a conditional evaluation structure (if ( $#cmd >= X && ... )) to support them.

  2. Subshell Array Indexing Fix: tcsh aggressively evaluates array indices like $cmd[2] even if the array is smaller than the requested index, causing "if: Empty if." errors. Added explicit bounds checking ($#cmd >= max_idx).

  3. Nested Subshell Safety: Standard shtab nests subshells using backticks which causes recursive parsing crashes in tcsh. Replaced with safe eval usage.

Fix:

It now adds completions for optional arguments that take parameters (nargs != 0) by appending a = version (e.g., --opt=).

if optional.nargs != 0:
    specials.extend(get_specials(optional, 'c', optional_str + '='))

The logic for recursing into sub-parsers was broadened. Previously, it only recursed if there were no "requirements" (positional arguments already met). Now, it recurses as long as the choices are a dictionary, enabling better support for complex nested sub-commands.

The completion script generation for sub-commands was significantly rewritten:

  • Condition-based logic: It replaced an "ugly hack" using shell || checks with explicit if conditions in tcsh.

  • Command line parsing: It now calculates conditions based on the length and content of the command line ($cmd).

  • Safety Padding: It adds a padding of empty strings ("" "" ...) to the $COMMAND_LINE before assigning it to $cmd. This prevents "Subscript out of range" errors when tcsh tries to access an index that doesn't exist yet.

  • Custom complete Support: It now handles arguments with a complete attribute (custom completion logic), converting them to tcsh patterns and using eval for function calls.

Finally, it ensures the specials list (which stores the completion rules) contains no duplicate rules while maintaining their original order:

specials = list(dict.fromkeys(specials))

Issues:

1. Subcommand Positional Completion: shtab lacks native support for auto-completing positional
   arguments that belong to subcommands in tcsh (e.g., `borg help <topic>`). This builds a
   conditional evaluation structure (`if ( $#cmd >= X && ... )`) to support them.

2. Subshell Array Indexing Fix: `tcsh` aggressively evaluates array indices like `$cmd[2]` even
   if the array is smaller than the requested index, causing "if: Empty if." errors. Added
   explicit bounds checking (`$#cmd >= max_idx`).

3. Nested Subshell Safety: Standard shtab nests subshells using backticks which causes recursive
   parsing crashes in tcsh. Replaced with safe `eval` usage.

Fix:

It now adds completions for optional arguments that take parameters (`nargs != 0`) by appending a `=` version (e.g., `--opt=`).
```python
if optional.nargs != 0:
    specials.extend(get_specials(optional, 'c', optional_str + '='))
```

The logic for recursing into sub-parsers was broadened. Previously, it only recursed if there were no "requirements" (positional arguments already met). Now, it recurses as long as the choices are a dictionary, enabling better support for complex nested sub-commands.

The completion script generation for sub-commands was significantly rewritten:

- **Condition-based logic**: It replaced an "ugly hack" using shell `||` checks with explicit `if` conditions in `tcsh`.

- **Command line parsing**: It now calculates conditions based on the length and content of the command line (`$cmd`).

- **Safety Padding**: It adds a padding of empty strings (`"" "" ...`) to the `$COMMAND_LINE` before assigning it to `$cmd`. This prevents "Subscript out of range" errors when `tcsh` tries to access an index that doesn't exist yet.

- **Custom `complete` Support**: It now handles arguments with a `complete` attribute (custom completion logic), converting them to `tcsh` patterns and using `eval` for function calls.

Finally, it ensures the `specials` list (which stores the completion rules) contains no duplicate rules while maintaining their original order:
```python
specials = list(dict.fromkeys(specials))
```
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 23, 2026

Codecov Report

❌ Patch coverage is 55.00000% with 9 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
shtab/__init__.py 55.00% 9 Missing ⚠️

📢 Thoughts on this report? Let us know!

@ThomasWaldmann
Copy link
Copy Markdown
Contributor Author

ThomasWaldmann commented Mar 23, 2026

Note:

  • code change was AI generated (as a side effect of working on borgbackup's tcsh completions, where the AI noticed some troubles while testing these changes)
  • I transformed the original monkeypatch to a minimal patch for shtab, removed cosmetic changes like python quotes, line wrapping, etc.
  • I did a quick test of the patched shtab code with borgbackup, it now works for subcommands.

This needs review by someone with a clue about tcsh and shtab (that's not me).

@ThomasWaldmann
Copy link
Copy Markdown
Contributor Author

ThomasWaldmann commented Mar 23, 2026

Maybe @simaoafonso-pwt could help here.

I set "allow edits by maintainers" - @simaoafonso-pwt if you are not maintainer here, feel free to copy whatever you like from here to an own PR.

@simaoafonso-pwt
Copy link
Copy Markdown
Contributor

It now adds completions for optional arguments that take parameters (nargs != 0) by appending a = version (e.g., --opt=).

This seems like a good feature, but it doesn't really work. I see it produces:

        'c/-/(- h o v)/' \
...
        'n/-o/f/' \
        'c/-o=/f/' \

The -o= needs to be include before c/-/..., otherwise it will not reach it.


For the rest of the changes, I don't have so many complex commands to test, if you say that borg works better, I'm good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants