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
+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;
}