new bindable readline command `execute-named-command', bound to M-x in emacs mode

This commit is contained in:
Chet Ramey
2023-11-06 09:44:01 -05:00
parent b30389070a
commit 511fef0f5c
10 changed files with 497 additions and 7 deletions
+27
View File
@@ -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
View File
@@ -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
+3 -1
View File
@@ -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. */
+7
View File
@@ -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
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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 */
+1
View File
@@ -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 },
+2
View File
@@ -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);
+33
View File
@@ -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;
+418
View File
@@ -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;
}