From: Chet Ramey Date: Sun, 18 Jan 2026 22:55:44 +0000 (-0500) Subject: readline's internal pager now accepts `f' and `d' commands like `less X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=c4b56ed9ac00424bde0f9ce3adfb7edb3d19a557;p=thirdparty%2Fbash.git readline's internal pager now accepts `f' and `d' commands like `less --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 7a4948a8..25c19cff 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -12580,3 +12580,19 @@ tests/comsub21.sub,tests/comsub23.sub,tests/comsub26.sub,tests/comsub27.sub tests/comsub28.sub - new file, contains basic tests for ${; command; } From a contribution by Kevin Pulo + + 1/16 + ---- +lib/readline/complete.c + - _rl_internal_pager, get_y_or_n: readline's internal completions + pager understands `f' as a synonym for ' ' and `d' to scroll by + half the screen height, similar to `less' + +lib/readline/complete.c + - last_completion_nmatch: new variable, holds the number of matches + returned by the last call to rl_complete, set to 0 or 1 as + appropriate when we explicitly set last_completion_failed to 0 or 1 + - rl_complete_internal: if the last completion attempt succeeded and + returned multiple matches, but an immediate subsequent completion + attempt returned a single match, insert the single match + From a report by Adam Purkrt in 5/2025 diff --git a/lib/readline/complete.c b/lib/readline/complete.c index 9abdedef..74e1b601 100644 --- a/lib/readline/complete.c +++ b/lib/readline/complete.c @@ -439,6 +439,10 @@ int rl_sort_completion_matches = 1; /* Local variable states what happened during the last completion attempt. */ static int completion_changed_buffer; static int last_completion_failed = 0; +static int last_completion_nmatch = 0; + +/* Does the vector of possible completions M contain a single match? */ +#define ONEMATCH(m) ((m) && (m)[0] && (m)[1] == 0) /* The result of the query to the user about displaying completion matches */ static int completion_y_or_n; @@ -480,6 +484,7 @@ int rl_possible_completions (int ignore, int invoking_key) { last_completion_failed = 0; + last_completion_nmatch = 1; rl_completion_invoking_key = invoking_key; return (rl_complete_internal ('?')); } @@ -611,6 +616,10 @@ get_y_or_n (int for_pager) return (2); if (for_pager && (c == 'q' || c == 'Q')) return (0); + if (for_pager && c == 'f') /* synonym for ' ' like less */ + return (1); + if (for_pager && c == 'd') + return (3); rl_ding (); } } @@ -628,6 +637,12 @@ _rl_internal_pager (int lines) return -1; else if (i == 2) return (lines - 1); + else if (i == 3) + { + int n; + n = _rl_screenheight / 2; + return (lines > n) ? (lines - n) : -1; + } else return 0; } @@ -1542,8 +1557,7 @@ postprocess_matches (char ***matchesp, int matching_filenames) munge the array, deleting matches as it desires. */ if (rl_ignore_some_completions_function && matching_filenames) { - for (nmatch = 1; matches[nmatch]; nmatch++) - ; + nmatch = vector_len (matches); (void)(*rl_ignore_some_completions_function) (matches); if (matches == 0 || matches[0] == 0) { @@ -1554,8 +1568,7 @@ postprocess_matches (char ***matchesp, int matching_filenames) else { /* If we removed some matches, recompute the common prefix. */ - for (i = 1; matches[i]; i++) - ; + i = vector_len (matches); if (i > 1 && i < nmatch) { t = matches[0]; @@ -2074,12 +2087,13 @@ rl_complete_internal (int what_to_do) int start, end, delimiter, found_quote, i, nontrivial_lcd, do_display; char *text, *saved_line_buffer; char quote_char; - int tlen, mlen, saved_last_completion_failed; + int tlen, mlen, saved_last_completion_failed, saved_last_completion_nmatch; complete_sigcleanarg_t cleanarg; /* state to clean up on signal */ RL_SETSTATE(RL_STATE_COMPLETING); saved_last_completion_failed = last_completion_failed; + saved_last_completion_nmatch = last_completion_nmatch; set_completion_defaults (what_to_do); @@ -2132,6 +2146,7 @@ rl_complete_internal (int what_to_do) FREE (saved_line_buffer); completion_changed_buffer = 0; last_completion_failed = 1; + last_completion_nmatch = 0; RL_UNSETSTATE(RL_STATE_COMPLETING); _rl_reset_completion_state (); return (0); @@ -2148,6 +2163,7 @@ rl_complete_internal (int what_to_do) FREE (saved_line_buffer); completion_changed_buffer = 0; last_completion_failed = 1; + last_completion_nmatch = 0; RL_UNSETSTATE(RL_STATE_COMPLETING); _rl_reset_completion_state (); return (0); @@ -2155,6 +2171,7 @@ rl_complete_internal (int what_to_do) if (matches && matches[0] && *matches[0]) last_completion_failed = 0; + last_completion_nmatch = vector_len (matches); do_display = 0; @@ -2215,12 +2232,21 @@ rl_complete_internal (int what_to_do) case '?': /* Let's try to insert a single match here if the last completion failed but this attempt returned a single match. */ - if (saved_last_completion_failed && matches[0] && *matches[0] && matches[1] == 0) + if (saved_last_completion_failed && ONEMATCH (matches)) { insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char); append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd); break; } + /* Or if the last completion succeeded but returned multiple matches and + this attempt returned a single match. */ + if (saved_last_completion_failed == 0 && saved_last_completion_nmatch > 1 && ONEMATCH (matches)) + { + insert_match (matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, "e_char); + append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd); + break; + } + /*FALLTHROUGH*/ case '%': /* used by menu_complete */