mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-06-25 23:03:09 +02:00
new bindable readline command `execute-named-command', bound to M-x in emacs mode
This commit is contained in:
@@ -7946,3 +7946,30 @@ subst.c
|
||||
single-element vector with a copy of the original word so we can
|
||||
add it to the result list unchanged.
|
||||
Inspired by https://savannah.gnu.org/support/?110948
|
||||
|
||||
11/3
|
||||
----
|
||||
lib/readline/text.c
|
||||
- readstr: set of functions to read a string from the keyboard, using
|
||||
rl_line_buffer for temporary storage, with minimal editing and an
|
||||
optional caller-supplied completion function. Doesn't use the
|
||||
callback framework yet since none of the functions are public
|
||||
- rl_execute_named_command: new bindable function to read a bindable
|
||||
command name (from the funmap_names array) and execute it, with
|
||||
command name completion on SPACE and TAB
|
||||
|
||||
lib/readline/readline.h
|
||||
- rl_execute_named_command: new extern declaration
|
||||
|
||||
lib/readline/rlprivate.h
|
||||
- new extern declarations for the readstr function framework
|
||||
|
||||
lib/readline/funmap.c
|
||||
- execute-named-command: new bindable function name
|
||||
|
||||
lib/readline/emacs_keymap.c
|
||||
- rl_execute_named_function: bound to M-x by default
|
||||
|
||||
lib/readline/doc/rluser.texi
|
||||
- execute-named-command: document as bindable function name with its
|
||||
default binding to M-x in emacs mode
|
||||
|
||||
+2
-2
@@ -6135,8 +6135,8 @@ The Bash @sc{posix} mode is described in @ref{Bash POSIX Mode}.
|
||||
|
||||
These are the @sc{posix} special builtins:
|
||||
@example
|
||||
@w{break : . continue eval exec exit export readonly return set}
|
||||
@w{shift trap unset}
|
||||
@w{break : . source continue eval exec exit export readonly return set}
|
||||
@w{shift times trap unset}
|
||||
@end example
|
||||
|
||||
@node Shell Variables
|
||||
|
||||
@@ -159,7 +159,9 @@ STREQ(const char *a, const char *b)
|
||||
static inline int
|
||||
STREQN(const char *a, const char *b, size_t n)
|
||||
{
|
||||
return ((n == 0) || ((a)[0] == (b)[0] && strncmp(a, b, n) == 0));
|
||||
return ((n == 0) ||
|
||||
(n == 1 && a[0] == b[0]) ||
|
||||
((a)[0] == (b)[0] && strncmp(a, b, n) == 0));
|
||||
}
|
||||
|
||||
/* More convenience definitions that possibly save system or libc calls. */
|
||||
|
||||
@@ -1912,6 +1912,13 @@ editing mode.
|
||||
|
||||
@end ifclear
|
||||
|
||||
@item execute-named-command (M-x)
|
||||
Read a bindable readline command name from the input and execute the
|
||||
function to which it's bound, as if the key sequence to which it was
|
||||
bound appeared in the input.
|
||||
If this function is supplied with a numeric argument, it passes that
|
||||
argument to the function it executes.
|
||||
|
||||
@end ftable
|
||||
|
||||
@node Readline vi Mode
|
||||
|
||||
@@ -5,7 +5,7 @@ Copyright (C) 1988-2023 Free Software Foundation, Inc.
|
||||
@set EDITION 8.3
|
||||
@set VERSION 8.3
|
||||
|
||||
@set UPDATED 4 October 2023
|
||||
@set UPDATED-MONTH October 2023
|
||||
@set UPDATED 3 November 2023
|
||||
@set UPDATED-MONTH November 2023
|
||||
|
||||
@set LASTCHANGE Wed Oct 4 10:57:26 EDT 2023
|
||||
@set LASTCHANGE Fri Nov 3 12:04:26 EDT 2023
|
||||
|
||||
@@ -448,7 +448,7 @@ KEYMAP_ENTRY_ARRAY emacs_meta_keymap = {
|
||||
{ ISFUNC, rl_upcase_word }, /* Meta-u */
|
||||
{ ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-v */
|
||||
{ ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-w */
|
||||
{ ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-x */
|
||||
{ ISFUNC, rl_execute_named_command }, /* Meta-x */
|
||||
{ ISFUNC, rl_yank_pop }, /* Meta-y */
|
||||
{ ISFUNC, (rl_command_func_t *)0x0 }, /* Meta-z */
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@ static const FUNMAP default_funmap[] = {
|
||||
{ "end-of-history", rl_end_of_history },
|
||||
{ "end-of-line", rl_end_of_line },
|
||||
{ "exchange-point-and-mark", rl_exchange_point_and_mark },
|
||||
{ "execute-named-command", rl_execute_named_command },
|
||||
{ "fetch-history", rl_fetch_history },
|
||||
{ "forward-backward-delete-char", rl_rubout_or_delete },
|
||||
{ "forward-byte", rl_forward_byte },
|
||||
|
||||
@@ -209,6 +209,8 @@ extern int rl_stop_output (int, int);
|
||||
extern int rl_abort (int, int);
|
||||
extern int rl_tty_status (int, int);
|
||||
|
||||
extern int rl_execute_named_command (int, int);
|
||||
|
||||
/* Bindable commands for incremental and non-incremental history searching. */
|
||||
extern int rl_history_search_forward (int, int);
|
||||
extern int rl_history_search_backward (int, int);
|
||||
|
||||
@@ -110,6 +110,27 @@ typedef struct __rl_search_context
|
||||
char *search_terminators;
|
||||
} _rl_search_cxt;
|
||||
|
||||
/* readstr flags */
|
||||
#define RL_READSTR_NOSPACE 0x01 /* don't insert space, use for completion */
|
||||
|
||||
typedef struct __rl_readstr_context
|
||||
{
|
||||
int flags;
|
||||
|
||||
int prevc;
|
||||
int lastc;
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
char mb[MB_LEN_MAX];
|
||||
char pmb[MB_LEN_MAX];
|
||||
#endif
|
||||
|
||||
int save_point;
|
||||
int save_mark;
|
||||
int save_line;
|
||||
|
||||
int (*compfunc) (struct __rl_readstr_context *, int);
|
||||
} _rl_readstr_cxt;
|
||||
|
||||
struct _rl_cmd {
|
||||
Keymap map;
|
||||
int count;
|
||||
@@ -461,6 +482,16 @@ extern int _rl_char_search_internal (int, int, int);
|
||||
#endif
|
||||
extern int _rl_set_mark_at_pos (int);
|
||||
|
||||
extern _rl_readstr_cxt *_rl_rscxt_alloc (int);
|
||||
extern void _rl_rscxt_dispose (_rl_readstr_cxt *, int);
|
||||
extern void _rl_free_saved_readstr_line (void);
|
||||
extern void _rl_unsave_saved_readstr_line (void);
|
||||
extern _rl_readstr_cxt *_rl_readstr_init (int, int);
|
||||
extern int _rl_readstr_cleanup (_rl_readstr_cxt *, int);
|
||||
extern void _rl_readstr_restore (_rl_readstr_cxt *);
|
||||
extern int _rl_readstr_getchar (_rl_readstr_cxt *);
|
||||
extern int _rl_readstr_dispatch (_rl_readstr_cxt *, int);
|
||||
|
||||
/* undo.c */
|
||||
extern UNDO_LIST *_rl_copy_undo_entry (UNDO_LIST *);
|
||||
extern UNDO_LIST *_rl_copy_undo_list (UNDO_LIST *);
|
||||
@@ -635,6 +666,8 @@ extern int _rl_term_autowrap;
|
||||
extern int _rl_optimize_typeahead;
|
||||
extern int _rl_keep_mark_active;
|
||||
|
||||
extern _rl_readstr_cxt *_rl_rscxt;
|
||||
|
||||
/* undo.c */
|
||||
extern int _rl_doing_an_undo;
|
||||
extern int _rl_undo_group_level;
|
||||
|
||||
@@ -1928,3 +1928,421 @@ rl_mark_active_p (void)
|
||||
{
|
||||
return (mark_active);
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Reading a string entered from the keyboard */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* A very simple set of functions to read a string from the keyboard using
|
||||
the line buffer as temporary storage. The caller can set a completion
|
||||
function to perform completion on TAB and SPACE. */
|
||||
|
||||
/* XXX - this is all very similar to the search stuff but with a different
|
||||
CXT. */
|
||||
|
||||
static HIST_ENTRY *_rl_saved_line_for_readstr;
|
||||
_rl_readstr_cxt *_rl_rscxt;
|
||||
|
||||
_rl_readstr_cxt *
|
||||
_rl_rscxt_alloc (int flags)
|
||||
{
|
||||
_rl_readstr_cxt *cxt;
|
||||
|
||||
cxt = (_rl_readstr_cxt *)xmalloc (sizeof (_rl_readstr_cxt));
|
||||
|
||||
cxt->flags = flags;
|
||||
|
||||
cxt->save_point = rl_point;
|
||||
cxt->save_mark = rl_mark;
|
||||
cxt->save_line = where_history ();
|
||||
|
||||
cxt->prevc = cxt->lastc = 0;
|
||||
|
||||
cxt->compfunc = NULL;
|
||||
|
||||
return cxt;
|
||||
}
|
||||
|
||||
void
|
||||
_rl_rscxt_dispose (_rl_readstr_cxt *cxt, int flags)
|
||||
{
|
||||
xfree (cxt);
|
||||
}
|
||||
|
||||
void
|
||||
_rl_free_saved_readstr_line ()
|
||||
{
|
||||
if (_rl_saved_line_for_readstr)
|
||||
_rl_free_saved_line (_rl_saved_line_for_readstr);
|
||||
_rl_saved_line_for_readstr = (HIST_ENTRY *)NULL;
|
||||
}
|
||||
|
||||
void
|
||||
_rl_unsave_saved_readstr_line ()
|
||||
{
|
||||
if (_rl_saved_line_for_readstr)
|
||||
_rl_unsave_line (_rl_saved_line_for_readstr);
|
||||
_rl_saved_line_for_readstr = (HIST_ENTRY *)NULL;
|
||||
}
|
||||
|
||||
_rl_readstr_cxt *
|
||||
_rl_readstr_init (int pchar, int flags)
|
||||
{
|
||||
_rl_readstr_cxt *cxt;
|
||||
char *p;
|
||||
|
||||
cxt = _rl_rscxt_alloc (flags);
|
||||
|
||||
rl_maybe_replace_line ();
|
||||
_rl_saved_line_for_readstr = _rl_alloc_saved_line ();
|
||||
|
||||
rl_undo_list = 0;
|
||||
|
||||
rl_line_buffer[0] = 0;
|
||||
rl_end = rl_point = 0;
|
||||
|
||||
p = _rl_make_prompt_for_search (pchar ? pchar : '@');
|
||||
rl_message ("%s", p);
|
||||
xfree (p);
|
||||
|
||||
_rl_rscxt = cxt;
|
||||
|
||||
return cxt;
|
||||
}
|
||||
|
||||
int
|
||||
_rl_readstr_cleanup (_rl_readstr_cxt *cxt, int r)
|
||||
{
|
||||
_rl_rscxt_dispose (cxt, 0);
|
||||
_rl_rscxt = 0;
|
||||
|
||||
return (r != 1);
|
||||
}
|
||||
|
||||
void
|
||||
_rl_readstr_restore (_rl_readstr_cxt *cxt)
|
||||
{
|
||||
_rl_unsave_saved_readstr_line (); /* restores rl_undo_list */
|
||||
rl_point = cxt->save_point;
|
||||
rl_mark = cxt->save_mark;
|
||||
rl_restore_prompt (); /* _rl_make_prompt_for_search saved it */
|
||||
rl_clear_message ();
|
||||
_rl_fix_point (1);
|
||||
}
|
||||
|
||||
int
|
||||
_rl_readstr_getchar (_rl_readstr_cxt *cxt)
|
||||
{
|
||||
int c;
|
||||
|
||||
cxt->prevc = cxt->lastc;
|
||||
|
||||
/* Read a key and decide how to proceed. */
|
||||
RL_SETSTATE(RL_STATE_MOREINPUT);
|
||||
c = cxt->lastc = rl_read_key ();
|
||||
RL_UNSETSTATE(RL_STATE_MOREINPUT);
|
||||
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
/* This ends up with C (and LASTC) being set to the last byte of the
|
||||
multibyte character. In most cases c == lastc == mb[0] */
|
||||
if (c >= 0 && MB_CUR_MAX > 1 && rl_byte_oriented == 0)
|
||||
c = cxt->lastc = _rl_read_mbstring (cxt->lastc, cxt->mb, MB_LEN_MAX);
|
||||
#endif
|
||||
|
||||
RL_CHECK_SIGNALS ();
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Process just-read character C according to readstr context CXT. Return -1
|
||||
if the caller should abort the read, 0 if we should break out of the
|
||||
loop, and 1 if we should continue to read characters. This can perform
|
||||
completion on the string read so far (stored in rl_line_buffer) if the
|
||||
caller has set up a completion function. The completion function can
|
||||
return -1 to indicate that we should abort the read. If we return -1
|
||||
we will call _rl_readstr_restore to clean up the state, leaving the caller
|
||||
to free the context. */
|
||||
int
|
||||
_rl_readstr_dispatch (_rl_readstr_cxt *cxt, int c)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (c < 0)
|
||||
c = CTRL ('C');
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case CTRL('W'):
|
||||
rl_unix_word_rubout (1, c);
|
||||
break;
|
||||
|
||||
case CTRL('U'):
|
||||
rl_unix_line_discard (1, c);
|
||||
break;
|
||||
|
||||
case CTRL('Q'):
|
||||
case CTRL('V'):
|
||||
n = rl_quoted_insert (1, c);
|
||||
if (n < 0)
|
||||
{
|
||||
_rl_readstr_restore (cxt);
|
||||
return -1;
|
||||
}
|
||||
cxt->lastc = rl_line_buffer[rl_point - 1]; /* preserve prevc */
|
||||
break;
|
||||
|
||||
case RETURN:
|
||||
case NEWLINE:
|
||||
return 0;
|
||||
|
||||
case CTRL('H'):
|
||||
case RUBOUT:
|
||||
if (rl_point == 0)
|
||||
{
|
||||
_rl_readstr_restore (cxt);
|
||||
return -1;
|
||||
}
|
||||
_rl_rubout_char (1, c);
|
||||
break;
|
||||
|
||||
case CTRL('C'):
|
||||
case CTRL('G'):
|
||||
rl_ding ();
|
||||
_rl_readstr_restore (cxt);
|
||||
return -1;
|
||||
|
||||
case ESC:
|
||||
/* Allow users to bracketed-paste text into the string.
|
||||
Similar code is in search.c:_rl_nsearch_dispatch(). */
|
||||
if (_rl_enable_bracketed_paste && ((n = _rl_nchars_available ()) >= (BRACK_PASTE_SLEN-1)))
|
||||
{
|
||||
if (_rl_read_bracketed_paste_prefix (c) == 1)
|
||||
rl_bracketed_paste_begin (1, c);
|
||||
else
|
||||
{
|
||||
c = rl_read_key (); /* get the ESC that got pushed back */
|
||||
_rl_insert_char (1, c);
|
||||
}
|
||||
}
|
||||
else
|
||||
_rl_insert_char (1, c);
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
if ((cxt->flags & RL_READSTR_NOSPACE) == 0)
|
||||
{
|
||||
_rl_insert_char (1, c);
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case TAB:
|
||||
/* Perform completion if the caller has set a completion function. */
|
||||
n = (cxt->compfunc) ? (*cxt->compfunc) (cxt, c) : _rl_insert_char (1, c);
|
||||
if (n < 0)
|
||||
{
|
||||
_rl_readstr_restore (cxt);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
|
||||
rl_insert_text (cxt->mb);
|
||||
else
|
||||
#endif
|
||||
_rl_insert_char (1, c);
|
||||
break;
|
||||
}
|
||||
|
||||
(*rl_redisplay_function) ();
|
||||
rl_deactivate_mark ();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Reading and Executing named commands */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* A completion generator for bindable readline command names. */
|
||||
static char *
|
||||
readcmd_completion_function (const char *text, int state)
|
||||
{
|
||||
static const char **cmdlist = NULL;
|
||||
static size_t lind, nlen;
|
||||
const char *cmdname;
|
||||
|
||||
if (state == 0)
|
||||
{
|
||||
if (cmdlist)
|
||||
free (cmdlist);
|
||||
|
||||
cmdlist = rl_funmap_names ();
|
||||
lind = 0;
|
||||
nlen = RL_STRLEN (text);
|
||||
}
|
||||
if (cmdlist == 0 || cmdlist[lind] == 0)
|
||||
return (char *)NULL;
|
||||
|
||||
while (cmdlist[lind])
|
||||
{
|
||||
cmdname = cmdlist[lind++];
|
||||
if (STREQN (text, cmdname, nlen))
|
||||
return (savestring (cmdname));
|
||||
}
|
||||
return ((char *)NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
_rl_display_cmdname_matches (char **matches)
|
||||
{
|
||||
size_t len, max, i;
|
||||
int old;
|
||||
|
||||
old = rl_filename_completion_desired;
|
||||
rl_filename_completion_desired = 0;
|
||||
|
||||
/* There is more than one match. Find out how many there are,
|
||||
and find the maximum printed length of a single entry. */
|
||||
for (max = 0, i = 1; matches[i]; i++)
|
||||
{
|
||||
len = strlen (matches[i]);
|
||||
|
||||
if (len > max)
|
||||
max = len;
|
||||
}
|
||||
len = i - 1;
|
||||
|
||||
rl_display_match_list (matches, len, max);
|
||||
rl_filename_completion_desired = old;
|
||||
|
||||
rl_forced_update_display ();
|
||||
rl_display_fixed = 1;
|
||||
}
|
||||
|
||||
static int
|
||||
_rl_readcmd_complete (_rl_readstr_cxt *cxt, int c)
|
||||
{
|
||||
char **matches;
|
||||
char *prefix;
|
||||
size_t plen;
|
||||
|
||||
matches = rl_completion_matches (rl_line_buffer, readcmd_completion_function);
|
||||
|
||||
if (RL_SIG_RECEIVED())
|
||||
{
|
||||
_rl_free_match_list (matches);
|
||||
matches = 0;
|
||||
RL_CHECK_SIGNALS ();
|
||||
return -1;
|
||||
}
|
||||
else if (matches == 0)
|
||||
rl_ding ();
|
||||
|
||||
/* Whether or not there are multiple matches, we just want to append the
|
||||
new characters in matches[0]. We display possible matches if we didn't
|
||||
append anything. */
|
||||
if (matches)
|
||||
{
|
||||
prefix = matches[0];
|
||||
plen = strlen (prefix);
|
||||
|
||||
if (plen > rl_end)
|
||||
{
|
||||
size_t n;
|
||||
for (n = rl_end; n < plen && prefix[n]; n++)
|
||||
_rl_insert_char (1, prefix[n]);
|
||||
}
|
||||
else if (matches[1])
|
||||
_rl_display_cmdname_matches (matches);
|
||||
_rl_free_match_list (matches);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Use the readstr functions to read a bindable command name using the
|
||||
line buffer, with completion. */
|
||||
static char *
|
||||
_rl_read_command_name ()
|
||||
{
|
||||
_rl_readstr_cxt *cxt;
|
||||
char *ret;
|
||||
int c, r;
|
||||
|
||||
cxt = _rl_readstr_init ('!', RL_READSTR_NOSPACE);
|
||||
cxt->compfunc = _rl_readcmd_complete;
|
||||
|
||||
/* skip callback stuff for now */
|
||||
r = 0;
|
||||
while (1)
|
||||
{
|
||||
c = _rl_readstr_getchar (cxt);
|
||||
|
||||
if (c < 0)
|
||||
{
|
||||
_rl_readstr_restore (cxt);
|
||||
_rl_readstr_cleanup (cxt, r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (c == 0)
|
||||
break;
|
||||
|
||||
r = _rl_readstr_dispatch (cxt, c);
|
||||
if (r < 0)
|
||||
{
|
||||
_rl_readstr_cleanup (cxt, r);
|
||||
return NULL; /* dispatch function cleans up */
|
||||
}
|
||||
else if (r == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
ret = savestring (rl_line_buffer);
|
||||
|
||||
/* Now restore the original line and perform one final redisplay. */
|
||||
_rl_readstr_restore (cxt);
|
||||
(*rl_redisplay_function) ();
|
||||
|
||||
/* And free up the context. */
|
||||
_rl_readstr_cleanup (cxt, r);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read a command name from the keyboard and execute it as if the bound key
|
||||
sequence had been entered. */
|
||||
int
|
||||
rl_execute_named_command (int count, int key)
|
||||
{
|
||||
char *command;
|
||||
rl_command_func_t *func;
|
||||
int r;
|
||||
|
||||
command = _rl_read_command_name ();
|
||||
if (command == 0 || *command == '\0')
|
||||
return 1;
|
||||
if (func = rl_named_function (command))
|
||||
{
|
||||
int prev, ostate;
|
||||
|
||||
prev = rl_dispatching;
|
||||
ostate = RL_ISSTATE (RL_STATE_DISPATCHING);
|
||||
rl_dispatching = 1;
|
||||
RL_SETSTATE (RL_STATE_DISPATCHING); /* make sure it's set */
|
||||
r = (*func) (count, key);
|
||||
if (ostate == 0)
|
||||
RL_UNSETSTATE (RL_STATE_DISPATCHING); /* unset it if it wasn't set */
|
||||
rl_dispatching = prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
rl_ding ();
|
||||
r = 1;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user