From bfb0f5cc5f9b2e95694057338b38110830740d92 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Wed, 18 Mar 2026 12:56:49 +0100 Subject: [PATCH] add tcsh completion support `borg completion tcsh` now generates a usable completion script: - add a tcsh preamble with the dynamic completion helpers (as aliases, since tcsh has no functions) and wire the tcsh patterns up for sort keys, files-cache mode, compression specs, chunker params, relative times, timestamps, file sizes and help topics. - complete archive names, archive IDs (when the token starts with "aid:") and tags. tcsh can neither define functions nor use backquotes there (the completion rule calling the helper is backquoted already), so both helpers run one POSIX sh script that parses $COMMAND_LINE for --repo/-r and queries `borg repo-list`. tcsh has no completion descriptions, so unlike zsh and fish these are plain candidate lists. The generator fixes this needed are all upstream in shtab now (tqdm/shtab#213, released in 1.9.3, which we already require): positional completion under subcommands at any depth, no out-of-range `$cmd` indexing, custom `.complete` patterns in multi-requirement rules, `--opt=` completion, and rule deduplication. One upstream fix is merged but not yet released (tqdm/shtab#241): completion patterns (`f`, `d`, ...) for a positional of a subcommand end up inside a `p@N@` rule, where tcsh runs the clauses as commands and only uses their output, so they do nothing - e.g. `borg umount ` would not complete a mountpoint. `_tcsh_anchor_positional_patterns` rewrites those into `n/` rules keyed off the preceding (sub)command word, producing exactly what a shtab with #241 generates. It is a no-op with such a shtab and can then be removed. Note that tcsh matches completions for positional arguments by word position, so options before an archive name shift it out of place and it is not completed - the `borg completion` epilog points at BORG_REPO for this. --- src/borg/archiver/completion_cmd.py | 120 +++++++++++++++++- .../testsuite/archiver/completion_cmd_test.py | 56 ++++++++ 2 files changed, 172 insertions(+), 4 deletions(-) diff --git a/src/borg/archiver/completion_cmd.py b/src/borg/archiver/completion_cmd.py index 6a39c8f79d..c50287735d 100644 --- a/src/borg/archiver/completion_cmd.py +++ b/src/borg/archiver/completion_cmd.py @@ -16,6 +16,7 @@ - Completes archive names by default (e.g., "my-backup-2024") - Completes archive IDs when prefixed with "aid:" (e.g., "aid:12345678") - In zsh and fish, shows archive metadata (name, timestamp, user@host) as descriptions + (tcsh has no completion descriptions) - Respects --repo/-r flags to query the correct repository 2. Sort keys (SortBySpec): @@ -54,6 +55,8 @@ - Suggests common file size values (500M, 1G, 10G, 100G, 1T, etc.) """ +import re + import shtab from ._common import process_epilog @@ -790,6 +793,103 @@ """ +TCSH_PREAMBLE_TMPL = r""" +# Dynamic completion helpers for tcsh + +alias _borg_complete_timestamp 'date +"%Y-%m-%dT%H:%M:%S"' + + +alias _borg_complete_sortby "echo {SORT_KEYS}" +alias _borg_complete_filescachemode "echo {FCM_KEYS}" +alias _borg_help_topics "echo {HELP_CHOICES}" +alias _borg_complete_compression_spec "echo {COMP_SPEC_CHOICES}" +alias _borg_complete_chunker_params "echo {CHUNKER_PARAMS_CHOICES}" +alias _borg_complete_relative_time "echo {RELATIVE_TIME_CHOICES}" +alias _borg_complete_file_size "echo {FILE_SIZE_CHOICES}" + +# Complete archive names (archive IDs when the current token starts with "aid:") and tags. +# These need the command line (for --repo/-r) and some logic, which tcsh cannot do itself: +# it has no functions, and an alias cannot use backquotes here because the completion rule +# calling the alias is backquoted already. So the work is done by a POSIX sh script, kept in +# a variable (a single-quoted csh string, hence no single quotes in it) and run via "sh -c". +set _borg_sh_complete = '{SH_COMPLETE}' + +alias _borg_complete_archive 'sh -c "$_borg_sh_complete" borg-completion archive "$COMMAND_LINE"' +alias _borg_complete_tags 'sh -c "$_borg_sh_complete" borg-completion tags "$COMMAND_LINE"' +""" + +# the sh script the tcsh preamble runs, as one line (`sh -c