From: Andre Noll Date: Mon, 28 Apr 2025 14:37:14 +0000 (+0200) Subject: i9e: Introduce i9e_get_nonopt_argnum(). X-Git-Url: https://git.tuebingen.mpg.de/?a=commitdiff_plain;h=9f4e112de873bca8f947e7e80ef5048172515f1b;p=paraslash.git i9e: Introduce i9e_get_nonopt_argnum(). Call the new function from various subcommand completers to attempt completion only on the first or the first two non-option arguments. --- diff --git a/audioc.c b/audioc.c index 6b98de4f..1fa2d76c 100644 --- a/audioc.c +++ b/audioc.c @@ -104,7 +104,8 @@ static void help_completer(struct i9e_completion_info *ci, i9e_complete_option(opts, ci, cr); return; } - cr->matches = i9e_complete_commands(ci->word, audiod_completers); + if (i9e_get_nonopt_argnum(opts, ci) == 1) + cr->matches = i9e_complete_commands(ci->word, audiod_completers); } static void ll_completer(struct i9e_completion_info *ci, diff --git a/client.c b/client.c index 00902e3d..4ae60f23 100644 --- a/client.c +++ b/client.c @@ -227,7 +227,9 @@ static void complete_mvblob(const char *blob_type, struct i9e_completion_info *ci, struct i9e_completion_result *cr) { - generic_blob_complete(blob_type, ci, cr); + unsigned num = i9e_get_nonopt_argnum(NULL, ci); + if (num == 1 || num == 2) + generic_blob_complete(blob_type, ci, cr); } /* these don't need any completions */ @@ -261,7 +263,8 @@ static void help_completer(struct i9e_completion_info *ci, i9e_complete_option(opts, ci, cr); return; } - cr->matches = i9e_complete_commands(ci->word, completers); + if (i9e_get_nonopt_argnum(opts, ci) == 1) /* arg #0 is "help" */ + cr->matches = i9e_complete_commands(ci->word, completers); } static void stat_completer(struct i9e_completion_info *ci, @@ -440,7 +443,9 @@ static void lsatt_completer(struct i9e_completion_info *ci, static void mvatt_completer(struct i9e_completion_info *ci, struct i9e_completion_result *cr) { - complete_attributes(ci->word, &cr->matches); + unsigned num = i9e_get_nonopt_argnum(NULL, ci); + if (num == 1 || num == 2) + complete_attributes(ci->word, &cr->matches); } static void rmatt_completer(struct i9e_completion_info *ci, @@ -497,7 +502,8 @@ static void select_completer(struct i9e_completion_info *ci, if (ci->word[0] == '-') return i9e_complete_option(opts, ci, cr); - complete_mops(ci->word, "", &cr->matches); + if (i9e_get_nonopt_argnum(opts, ci) == 1) /* arg #0 is "select" */ + complete_mops(ci->word, "", &cr->matches); } #define DEFINE_BLOB_COMPLETER(cmd, blob_type) \ diff --git a/interactive.c b/interactive.c index 0b1a978f..18f9eebd 100644 --- a/interactive.c +++ b/interactive.c @@ -720,6 +720,77 @@ void i9e_complete_option(char * const *opts, } } +/** + * Return the number of non-option arguments encountered so far. + * + * This counts the number of arguments up to the current word which are neither + * options nor arguments to options. + * + * \param opts NULL terminated array of short and long options, may be NULL. + * \param ci Provided by i9e in completion context. + * + * For convenience, NULL can be passed as the option array pointer to + * indicate that the subcommand has no options. In this case the function + * returns the word number stored in the completion info structure. + * + * \return Zero means that the cursor is positioned at the first non-option + * argument. + */ +unsigned i9e_get_nonopt_argnum(char * const *opts, + struct i9e_completion_info *ci) +{ + bool prev_is_option_with_arg = false; + unsigned num_non_option_args = 0; + + if (!opts) + return ci->word_num; + for (unsigned n = 0; n < ci->word_num; n++) { + unsigned m; + const char *arg = ci->argv[n]; + + if (prev_is_option_with_arg) { + prev_is_option_with_arg = false; + continue; + } + if (!strcmp(arg, "--")) { + prev_is_option_with_arg = false; + continue; + } + if (arg[0] != '-') { + num_non_option_args++; + prev_is_option_with_arg = false; + continue; + } + for (m = 0; opts[m]; m++) { + const char *opt = opts[m]; + size_t len = strlen(opt); + if (opt[len - 1] != '=') { + if (strcmp(opt, arg)) + continue; + /* opt without arg */ + prev_is_option_with_arg = false; + break; + } + if (strncmp(opt, arg, len - 1)) + continue; + if (arg[len - 1] == '\0') { /* --opt-with-arg */ + prev_is_option_with_arg = true; + break; + } + if (arg[len - 1] == '=') { /* --opt-with-arg= */ + prev_is_option_with_arg = false; + break; + } + /* --opt-with-arg-garbage, no match */ + } + if (!opts[m]) { /* no match */ + num_non_option_args++; + prev_is_option_with_arg = false; + } + } + return num_non_option_args; +} + /** * Find out whether the current word is an argument to an option. * diff --git a/interactive.h b/interactive.h index 6d84d226..95d19560 100644 --- a/interactive.h +++ b/interactive.h @@ -90,6 +90,8 @@ int i9e_extract_completions(const char *word, char * const *string_list, char **i9e_complete_commands(const char *word, struct i9e_completer *completers); void i9e_complete_option(char * const *opts, struct i9e_completion_info *ci, struct i9e_completion_result *cr); +unsigned i9e_get_nonopt_argnum(char * const *opts, + struct i9e_completion_info *ci); int i9e_cword_is_option_arg(char * const *opts, struct i9e_completion_info *ci); int i9e_print_completions(struct i9e_completer *completers); int i9e_get_error(void);