From: Andre Noll Date: Sun, 6 Apr 2025 20:14:43 +0000 (+0200) Subject: i9e: Introduce i9e_cword_is_option_arg(). X-Git-Url: https://git.tuebingen.mpg.de/?a=commitdiff_plain;h=65c4d2835975203a45fadde20bae7a4f1b8dbd85;p=paraslash.git i9e: Introduce i9e_cword_is_option_arg(). Call the new function to complete --admissible, --sort and --listing-mode of the ls subcommand and teach the touch completer to prevent filename completion if a numerical argument is expected. The grab subcommand of para_audiod also benefits from the new helper: para_audioc learned to complete the three different grab modes. Since --admissible expects a mood or playlist argument, factor out the code from the select completer which returns the list of all moods and playlists so that it can be called by the ls completer as well. --- diff --git a/audioc.c b/audioc.c index 45b5e12c..6b98de4f 100644 --- a/audioc.c +++ b/audioc.c @@ -138,6 +138,19 @@ static void grab_completer(struct i9e_completion_info *ci, struct i9e_completion_result *cr) { char * const opts[] = {LSG_AUDIOD_CMD_GRAB_OPTS, NULL}; + int num = i9e_cword_is_option_arg(opts, ci); + + if (num >= 0) { + const char *opt = opts[num]; + if (!strcmp(opt, "-m=") || !strcmp(opt, "--mode=")) { + cr->matches = arr_alloc(4, sizeof(char *)); + cr->matches[0] = make_message("%ss", opt); + cr->matches[1] = make_message("%sp", opt); + cr->matches[2] = make_message("%sa", opt); + cr->matches[3] = NULL; + return; + } + } i9e_complete_option(opts, ci, cr); } diff --git a/client.c b/client.c index a0def4ae..00902e3d 100644 --- a/client.c +++ b/client.c @@ -318,10 +318,79 @@ static void add_completer(struct i9e_completion_info *ci, cr->filename_completion_desired = true; } +static void complete_mops(const char *word, const char *pfx, char ***matches) +{ + char *mood_buf, *pl_buf, **moods, **playlists; + char **mops; + int num_moods, num_pl, i, n, ret; + + ret = execute_client_command("lsmood", &mood_buf); + if (ret < 0) + return; + ret = execute_client_command("lspl", &pl_buf); + if (ret < 0) + goto free_mood_buf; + ret = create_argv(mood_buf, "\n", &moods); + if (ret < 0) + goto free_pl_buf; + num_moods = ret; + ret = create_argv(pl_buf, "\n", &playlists); + if (ret < 0) + goto free_moods; + num_pl = ret; + n = num_moods + num_pl; + mops = arr_alloc(n + 1, sizeof(char *)); + for (i = 0; i < num_moods; i++) + mops[i] = make_message("%sm/%s", pfx, moods[i]); + for (i = 0; i < num_pl; i++) + mops[num_moods + i] = make_message("%sp/%s", pfx, playlists[i]); + mops[n] = NULL; + i9e_extract_completions(word, mops, matches); + free_argv(mops); + free_argv(playlists); +free_moods: + free_argv(moods); +free_pl_buf: + free(pl_buf); +free_mood_buf: + free(mood_buf); +} + static void ls_completer(struct i9e_completion_info *ci, struct i9e_completion_result *cr) { char * const opts[] = {LSG_SERVER_CMD_LS_OPTS, NULL}; + int num = i9e_cword_is_option_arg(opts, ci); + + if (num >= 0) { + const char *opt = opts[num]; + if (!strcmp(opt, "-a=") || !strcmp(opt, "--admissible=")) + return complete_mops(ci->word, opt, &cr->matches); + if (!strcmp(opt, "-l=") || !strcmp(opt, "--listing-mode=")) { + cr->matches = arr_alloc(5, sizeof(char *)); + cr->matches[0] = make_message("%ss", opt); + cr->matches[1] = make_message("%sl", opt); + cr->matches[2] = make_message("%sv", opt); + cr->matches[3] = make_message("%sp", opt); + cr->matches[4] = NULL; + return; + } + if (!strcmp(opt, "-s=") || !strcmp(opt, "--sort=")) { + cr->matches = arr_alloc(11, sizeof(char *)); + cr->matches[0] = make_message("%sl", opt); + cr->matches[1] = make_message("%sn", opt); + cr->matches[2] = make_message("%sf", opt); + cr->matches[3] = make_message("%sc", opt); + cr->matches[4] = make_message("%si", opt); + cr->matches[5] = make_message("%sy", opt); + cr->matches[6] = make_message("%sb", opt); + cr->matches[7] = make_message("%sd", opt); + cr->matches[8] = make_message("%sa", opt); + cr->matches[9] = make_message("%sh", opt); + cr->matches[10] = NULL; + return; + } + } if (ci->word[0] == '-') i9e_complete_option(opts, ci, cr); cr->filename_completion_desired = true; @@ -404,8 +473,10 @@ static void touch_completer(struct i9e_completion_info *ci, { char * const opts[] = {LSG_SERVER_CMD_TOUCH_OPTS, NULL}; + if (i9e_cword_is_option_arg(opts, ci) >= 0) + return; if (ci->word[0] == '-') - i9e_complete_option(opts, ci, cr); + return i9e_complete_option(opts, ci, cr); cr->filename_completion_desired = true; } @@ -423,42 +494,10 @@ static void select_completer(struct i9e_completion_info *ci, struct i9e_completion_result *cr) { char * const opts[] = {LSG_SERVER_CMD_SELECT_OPTS, NULL}; - char *mood_buf, *pl_buf, **moods, **playlists, **mops; - int num_moods, num_pl, i, n, ret; if (ci->word[0] == '-') return i9e_complete_option(opts, ci, cr); - ret = execute_client_command("lsmood", &mood_buf); - if (ret < 0) - return; - ret = execute_client_command("lspl", &pl_buf); - if (ret < 0) - goto free_mood_buf; - - ret = create_argv(mood_buf, "\n", &moods); - if (ret < 0) - goto free_pl_buf; - num_moods = ret; - ret = create_argv(pl_buf, "\n", &playlists); - if (ret < 0) - goto free_moods; - num_pl = ret; - n = num_moods + num_pl; - mops = arr_alloc(n + 1, sizeof(char *)); - for (i = 0; i < num_moods; i++) - mops[i] = make_message("m/%s", moods[i]); - for (i = 0; i < num_pl; i++) - mops[num_moods + i] = make_message("p/%s", playlists[i]); - mops[n] = NULL; - i9e_extract_completions(ci->word, mops, &cr->matches); - free_argv(mops); - free_argv(playlists); -free_moods: - free_argv(moods); -free_pl_buf: - free(pl_buf); -free_mood_buf: - free(mood_buf); + complete_mops(ci->word, "", &cr->matches); } #define DEFINE_BLOB_COMPLETER(cmd, blob_type) \ diff --git a/interactive.c b/interactive.c index 77f3fc60..0b1a978f 100644 --- a/interactive.c +++ b/interactive.c @@ -720,6 +720,44 @@ void i9e_complete_option(char * const *opts, } } +/** + * Find out whether the current word is an argument to an option. + * + * This handles both the --opt=arg and --opt arg syntax to specify option + * arguments. + * + * \param opts NULL-terminated array of short and long options. + * \param ci Provided by i9e in completion context. + * + * \return If the current word is an option argument, the function returns + * the index into the option array which corresponds to the matching option. + * Otherwise, -1 is returned. + */ +int i9e_cword_is_option_arg(char * const *opts, struct i9e_completion_info *ci) +{ + const char *prev; + + /* Return -1 if cursor is at first word or if we've seen "--". */ + if (ci->word_num == 0) + return -1; + for (unsigned n = 0; n < ci->word_num; n++) + if (!strcmp(ci->argv[n], "--")) + return -1; + prev = ci->argv[ci->word_num - 1]; + for (unsigned n = 0; opts[n]; n++) { + const char *opt = opts[n]; + size_t len = strlen(opt); + assert(len > 0); + if (opt[len - 1] != '=') + continue; + if (!strncmp(opt, ci->word, len)) + return n; /* --opt=arg */ + if (!strncmp(opt, prev, len - 1) && prev[len - 1] == '\0') + return n; /* --opt arg */ + } + return -1; +} + /** * Print possible completions to stdout. * diff --git a/interactive.h b/interactive.h index 7ab9778f..6d84d226 100644 --- a/interactive.h +++ b/interactive.h @@ -90,6 +90,7 @@ 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); +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); void i9e_ll_completer(struct i9e_completion_info *ci,