Fix libedit history behaviour

This commit is contained in:
Přemysl Eric Janouch 2022-09-04 14:57:20 +02:00
parent 3cf3c0215e
commit c1b6918db3
Signed by: p
GPG Key ID: A0420B94F92B9493
3 changed files with 38 additions and 13 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) 2014 - 2020, Přemysl Eric Janouch <p@janouch.name> Copyright (c) 2014 - 2022, Přemysl Eric Janouch <p@janouch.name>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted. purpose with or without fee is hereby granted.

View File

@ -140,8 +140,7 @@ the higher-level protocol (the "Sec-Ws-Protocol" HTTP field).
Bugs Bugs
---- ----
The editline (libedit) frontend is more of a proof of concept that mostly seems The editline (libedit) frontend may exhibit some unexpected behaviour.
to work but exhibits bugs that are not our fault.
Examples Examples
-------- --------

View File

@ -1,7 +1,7 @@
/* /*
* json-rpc-shell.c: a shell for JSON-RPC 2.0 * json-rpc-shell.c: a shell for JSON-RPC 2.0
* *
* Copyright (c) 2014 - 2020, Přemysl Eric Janouch <p@janouch.name> * Copyright (c) 2014 - 2022, Přemysl Eric Janouch <p@janouch.name>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted. * purpose with or without fee is hereby granted.
@ -515,6 +515,7 @@ struct input_el
char *entered_line; ///< Buffers the entered line char *entered_line; ///< Buffers the entered line
bool active; ///< Interface has been started bool active; ///< Interface has been started
bool need_restart; ///< Need to clear history state
char *prompt; ///< The prompt we use char *prompt; ///< The prompt we use
int prompt_shown; ///< Whether the prompt is shown now int prompt_shown; ///< Whether the prompt is shown now
@ -540,12 +541,24 @@ input_el_redisplay (struct input_el *self)
{ {
// See rl_redisplay(), however NetBSD editline's map.c v1.54 breaks VREPRINT // See rl_redisplay(), however NetBSD editline's map.c v1.54 breaks VREPRINT
// so we bind redisplay somewhere else in input_el_start() // so we bind redisplay somewhere else in input_el_start()
char x[] = { 'q' & 31, 0 }; wchar_t x[] = { L'q' & 31, 0 };
el_push (self->editline, x); el_wpush (self->editline, x);
// We have to do this or it gets stuck and nothing is done // We have to do this or it gets stuck and nothing is done
int count = 0; int dummy_count = 0;
(void) el_wgets (self->editline, &count); (void) el_wgets (self->editline, &dummy_count);
}
// Editline keeping its own history position (look for "eventno" there).
// This is the only sane way of resetting it.
static void
input_el_start_over (struct input_el *self)
{
wchar_t x[] = { L'c' & 31, 0 };
el_wpush (self->editline, x);
int dummy_count = 0;
(void) el_wgets (self->editline, &dummy_count);
} }
static char * static char *
@ -603,13 +616,14 @@ input_el_on_return (EditLine *editline, int key)
self->entered_line = xstrndup self->entered_line = xstrndup
(info_mb->buffer, info_mb->lastchar - info_mb->buffer); (info_mb->buffer, info_mb->lastchar - info_mb->buffer);
// Now we need to force editline to actually print the newline // Now we need to force editline into actually printing the newline
el_cursor (editline, len++ - point); el_cursor (editline, len++ - point);
el_insertstr (editline, "\n"); el_insertstr (editline, "\n");
input_el_redisplay (self); input_el_redisplay (self);
// Finally we need to discard the old line's contents // Finally we need to discard the old line's contents
el_wdeletestr (editline, len); el_wdeletestr (editline, len);
self->need_restart = true;
return CC_NEWLINE; return CC_NEWLINE;
} }
@ -673,8 +687,6 @@ input_el_start (struct input *input, const char *program_name)
el_set (self->editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); el_set (self->editline, EL_BIND, "^w", "ed-delete-prev-word", NULL);
// Just what are you doing? // Just what are you doing?
el_set (self->editline, EL_BIND, "^u", "vi-kill-line-prev", NULL); el_set (self->editline, EL_BIND, "^u", "vi-kill-line-prev", NULL);
// See input_el_redisplay(), functionally important
el_set (self->editline, EL_BIND, "^q", "ed-redisplay", NULL);
// It's probably better to handle these ourselves // It's probably better to handle these ourselves
input_el_addbind (self->editline, "send-line", "Send line", input_el_addbind (self->editline, "send-line", "Send line",
@ -690,6 +702,11 @@ input_el_start (struct input *input, const char *program_name)
// Source the user's defaults file // Source the user's defaults file
el_source (self->editline, NULL); el_source (self->editline, NULL);
// See input_el_redisplay(), functionally important
el_set (self->editline, EL_BIND, "^q", "ed-redisplay", NULL);
// This is what buffered el_wgets() does, functionally important
el_set (self->editline, EL_BIND, "^c", "ed-start-over", NULL);
self->active = true; self->active = true;
self->prompt_shown = 1; self->prompt_shown = 1;
} }
@ -977,6 +994,16 @@ input_el_on_tty_readable (struct input *input)
// We bind the return key to process it how we need to // We bind the return key to process it how we need to
struct input_el *self = (struct input_el *) input; struct input_el *self = (struct input_el *) input;
int unbuffered = 0;
(void) el_get (self->editline, EL_UNBUFFERED, &unbuffered);
// We must invoke ch_reset(), which isn't done for us with EL_UNBUFFERED.
if (unbuffered && self->need_restart)
{
self->need_restart = false;
input_el_start_over (self);
}
// el_gets() with EL_UNBUFFERED doesn't work with UTF-8, // el_gets() with EL_UNBUFFERED doesn't work with UTF-8,
// we must use the wide-character interface // we must use the wide-character interface
int count = 0; int count = 0;
@ -984,8 +1011,7 @@ input_el_on_tty_readable (struct input *input)
// Editline works in a funny NO_TTY mode when the input is not a tty, // Editline works in a funny NO_TTY mode when the input is not a tty,
// we cannot use EL_UNBUFFERED and expect sane results then // we cannot use EL_UNBUFFERED and expect sane results then
int unbuffered = 0; if (!unbuffered)
if (!el_get (self->editline, EL_UNBUFFERED, &unbuffered) && !unbuffered)
{ {
char *entered_line = buf ? input_el_wcstombs (buf) : NULL; char *entered_line = buf ? input_el_wcstombs (buf) : NULL;
self->super.on_input (entered_line, self->super.user_data); self->super.on_input (entered_line, self->super.user_data);