From e9650a80dd346f499f7781947ca66b6065acf3f1 Mon Sep 17 00:00:00 2001 Message-Id: From: Greg Price Date: Fri, 24 Apr 2020 21:32:19 -0700 Subject: [PATCH] Promote operate-and-get next (C-o) from Bash into Readline. This is a feature I'd love to have in some other Readline-using programs. Happily it doesn't involve anything particularly specific to the syntax or semantics of Bash, or of shells in general; its behavior is purely in the realm of history and editing. So, mostly this patch just moves the implementation from bashline.c into lib/readline/, with some renaming to fit being part of Readline. The one other substantive change is that we invoke the saved startup hook, in case the application has set something there which it's important to run on each prompt. We could do that either before or after restoring the desired line from the history; we pick before, mainly because that's what the one example in the Bash tree (in set_itext in builtins/read.def) does. Tested by trying out rltest, and also an rltest modified so that it uses a startup hook in the same way as `examples/rl -d hello`. --- bashline.c | 40 ------------------------------------ lib/readline/doc/rluser.texi | 12 +++++------ lib/readline/emacs_keymap.c | 2 +- lib/readline/funmap.c | 1 + lib/readline/misc.c | 40 ++++++++++++++++++++++++++++++++++++ lib/readline/readline.h | 1 + 6 files changed, 49 insertions(+), 47 deletions(-) diff --git bashline.c bashline.c index 8d7c1c1aa..6cc9555ad 100644 --- bashline.c +++ bashline.c @@ -118,7 +118,6 @@ extern int tputs PARAMS((const char *string, int nlines, int (*outx)(int))); /* Functions bound to keys in Readline for Bash users. */ static int shell_expand_line PARAMS((int, int)); static int display_shell_version PARAMS((int, int)); -static int operate_and_get_next PARAMS((int, int)); static int bash_ignore_filenames PARAMS((char **)); static int bash_ignore_everything PARAMS((char **)); @@ -479,7 +478,6 @@ initialize_readline () /* Backwards compatibility. */ rl_add_defun ("insert-last-argument", rl_yank_last_arg, -1); - rl_add_defun ("operate-and-get-next", operate_and_get_next, -1); rl_add_defun ("display-shell-version", display_shell_version, -1); rl_add_defun ("edit-and-execute-command", emacs_edit_and_execute_command, -1); @@ -517,7 +515,6 @@ initialize_readline () rl_bind_key_if_unbound_in_map ('^', history_expand_line, emacs_meta_keymap); #endif - rl_bind_key_if_unbound_in_map (CTRL ('O'), operate_and_get_next, emacs_standard_keymap); rl_bind_key_if_unbound_in_map (CTRL ('V'), display_shell_version, emacs_ctlx_keymap); /* In Bash, the user can switch editing modes with "set -o [vi emacs]", @@ -920,43 +917,6 @@ hostnames_matching (text) return (result); } -/* The equivalent of the Korn shell C-o operate-and-get-next-history-line - editing command. */ -static int saved_history_logical_offset = -1; - -#define HISTORY_FULL() (history_is_stifled () && history_length >= history_max_entries) - -static int -set_saved_history () -{ - int absolute_offset, count; - - if (saved_history_logical_offset >= 0) - { - absolute_offset = saved_history_logical_offset - history_base; - count = where_history () - absolute_offset; - rl_get_previous_history (count, 0); - } - saved_history_logical_offset = -1; - rl_startup_hook = old_rl_startup_hook; - return (0); -} - -static int -operate_and_get_next (count, c) - int count, c; -{ - /* Accept the current line. */ - rl_newline (1, c); - - saved_history_logical_offset = rl_explicit_arg ? count : where_history () + history_base + 1; - - old_rl_startup_hook = rl_startup_hook; - rl_startup_hook = set_saved_history; - - return 0; -} - /* This vi mode command causes VI_EDIT_COMMAND to be run on the current command being entered (if no explicit argument is given), otherwise on a command from the history file. */ diff --git lib/readline/doc/rluser.texi lib/readline/doc/rluser.texi index 33bbb7983..5163024e6 100644 --- lib/readline/doc/rluser.texi +++ lib/readline/doc/rluser.texi @@ -1734,6 +1734,12 @@ If a numeric argument causes the comment character to be removed, the line will be executed by the shell. @end ifset +@item operate-and-get-next (C-o) +Accept the current line as if a newline had been typed, and fetch the next line +relative to the current line from the history for editing. +A numeric argument, if supplied, specifies the history entry to use instead +of the current line. + @item dump-functions () Print all of the functions and their key bindings to the Readline output stream. If a numeric argument is supplied, @@ -1794,12 +1800,6 @@ Perform history and alias expansion on the current line. @item insert-last-argument (M-. or M-_) A synonym for @code{yank-last-arg}. -@item operate-and-get-next (C-o) -Accept the current line for execution and fetch the next line -relative to the current line from the history for editing. -A numeric argument, if supplied, specifies the history entry to use instead -of the current line. - @item edit-and-execute-command (C-x C-e) Invoke an editor on the current command line, and execute the result as shell commands. diff --git lib/readline/emacs_keymap.c lib/readline/emacs_keymap.c index a508b6956..02597dad3 100644 --- lib/readline/emacs_keymap.c +++ lib/readline/emacs_keymap.c @@ -47,7 +47,7 @@ KEYMAP_ENTRY_ARRAY emacs_standard_keymap = { { ISFUNC, rl_clear_screen }, /* Control-l */ { ISFUNC, rl_newline }, /* Control-m */ { ISFUNC, rl_get_next_history }, /* Control-n */ - { ISFUNC, (rl_command_func_t *)0x0 }, /* Control-o */ + { ISFUNC, rl_operate_and_get_next }, /* Control-o */ { ISFUNC, rl_get_previous_history }, /* Control-p */ { ISFUNC, rl_quoted_insert }, /* Control-q */ { ISFUNC, rl_reverse_search_history }, /* Control-r */ diff --git lib/readline/funmap.c lib/readline/funmap.c index 73495610c..eca49a3e4 100644 --- lib/readline/funmap.c +++ lib/readline/funmap.c @@ -117,6 +117,7 @@ static const FUNMAP default_funmap[] = { { "non-incremental-forward-search-history-again", rl_noninc_forward_search_again }, { "non-incremental-reverse-search-history-again", rl_noninc_reverse_search_again }, { "old-menu-complete", rl_old_menu_complete }, + { "operate-and-get-next", rl_operate_and_get_next }, { "overwrite-mode", rl_overwrite_mode }, #if defined (_WIN32) { "paste-from-clipboard", rl_paste_from_clipboard }, diff --git lib/readline/misc.c lib/readline/misc.c index e6b42ebf2..0faec057a 100644 --- lib/readline/misc.c +++ lib/readline/misc.c @@ -637,6 +637,46 @@ rl_get_previous_history (int count, int key) return 0; } +static rl_hook_func_t *_rl_saved_history_old_startup_hook = (rl_hook_func_t *)NULL; +static int _rl_saved_history_logical_offset = -1; + +static int +_rl_set_saved_history () +{ + int r, absolute_offset, count; + + r = 0; + if (_rl_saved_history_old_startup_hook) + r = (*_rl_saved_history_old_startup_hook) (); + + if (_rl_saved_history_logical_offset >= 0) + { + absolute_offset = _rl_saved_history_logical_offset - history_base; + count = where_history () - absolute_offset; + rl_get_previous_history (count, 0); + } + + _rl_saved_history_logical_offset = -1; + rl_startup_hook = _rl_saved_history_old_startup_hook; + + return r; +} + +int +rl_operate_and_get_next (int count, int c) +{ + /* Accept the current line. */ + rl_newline (1, c); + + _rl_saved_history_logical_offset = + rl_explicit_arg ? count : where_history () + history_base + 1; + + _rl_saved_history_old_startup_hook = rl_startup_hook; + rl_startup_hook = _rl_set_saved_history; + + return 0; +} + /* **************************************************************** */ /* */ /* Editing Modes */ diff --git lib/readline/readline.h lib/readline/readline.h index 71b1b0cf2..8e6ec5379 100644 --- lib/readline/readline.h +++ lib/readline/readline.h @@ -133,6 +133,7 @@ extern int rl_beginning_of_history PARAMS((int, int)); extern int rl_end_of_history PARAMS((int, int)); extern int rl_get_next_history PARAMS((int, int)); extern int rl_get_previous_history PARAMS((int, int)); +extern int rl_operate_and_get_next PARAMS((int, int)); /* Bindable commands for managing the mark and region. */ extern int rl_set_mark PARAMS((int, int)); -- 2.20.1