From c0996fcbe75bd9003476337434ffc205749c3bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Sat, 27 Aug 2022 14:35:07 +0200 Subject: [PATCH] xC: normalize BSD Editline's history behaviour Now it's a realistically useful frontend. --- NEWS | 2 ++ README.adoc | 3 --- xC.adoc | 3 +-- xC.c | 53 +++++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 46 insertions(+), 15 deletions(-) diff --git a/NEWS b/NEWS index c7a05c6..207ec9b 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,8 @@ Unreleased * xC: improved pager integration capabilities + * xC: normalized editline's history behaviour, making it a viable frontend + * xC: made it show WALLOPS messages, as PRIVMSG for the server buffer * xD: implemented WALLOPS, choosing to make it target even non-operators diff --git a/README.adoc b/README.adoc index 7112a41..9d61d8c 100644 --- a/README.adoc +++ b/README.adoc @@ -77,9 +77,6 @@ Runtime dependencies: openssl + Additionally for 'xC': curses, libffi, lua >= 5.3 (optional), readline >= 6.0 or libedit >= 2013-07-12 -Avoid libedit if you can, in general it works but at the moment history is -acting up and I have no clue about fixing it. - $ git clone --recursive https://git.janouch.name/p/xK.git $ mkdir xK/build $ cd xK/build diff --git a/xC.adoc b/xC.adoc index ba5c4c4..b2caa6d 100644 --- a/xC.adoc +++ b/xC.adoc @@ -114,8 +114,7 @@ _/usr/share/xC/plugins/_:: Bugs ---- -The editline (libedit) frontend is more of a proof of concept that mostly seems -to work but exhibits bugs that are not our fault. +The editline (libedit) frontend may exhibit some unexpected behaviour. Reporting bugs -------------- diff --git a/xC.c b/xC.c index 535fbd9..ab51fae 100644 --- a/xC.c +++ b/xC.c @@ -761,6 +761,7 @@ struct input_el { struct input super; ///< Parent class EditLine *editline; ///< The EditLine object + FILE *null; ///< Output redirect bool active; ///< Are we a thing? char *prompt; ///< The prompt we use @@ -778,12 +779,12 @@ input_el__redisplay (void *input) // See rl_redisplay(), however NetBSD editline's map.c v1.54 breaks VREPRINT // so we bind redisplay somewhere else in app_editline_init() struct input_el *self = input; - char x[] = { 'q' & 31, 0 }; - el_push (self->editline, x); + wchar_t x[] = { L'q' & 31, 0 }; + el_wpush (self->editline, x); // We have to do this or it gets stuck and nothing is done - int count = 0; - (void) el_wgets (self->editline, &count); + int dummy_count = 0; + (void) el_wgets (self->editline, &dummy_count); } static char * @@ -1026,18 +1027,50 @@ input_el__restore (struct input_el *self) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// XXX: Editline keeping its own history position (look for "eventno" there). +// Invoking ed-next-history through our bind from app_editline_init() seems +// like the only viable hack to get consistent history behaviour. +static void +input_el__bottom (struct input_el *self) +{ + // First, we need to redirect output to avoid ringing the terminal bell. + FILE *out = NULL; + el_wget (self->editline, EL_GETFP, 1, &out); + el_wset (self->editline, EL_SETFP, 1, self->null); + + // Invoke hist_get() to make the history pointer's cursor match "eventno". + int down = 1, dummy_count = 0; + el_wpush (self->editline, L"\x1bn"); + (void) el_wgets (self->editline, &dummy_count); + + // It doesn't seem like we can just retrieve the position. + HistEventW ev; + while (!history_w (self->current->history, &ev, H_PREV)) + down++; + while (down--) + { + el_wpush (self->editline, L"\x1bn"); + (void) el_wgets (self->editline, &dummy_count); + } + + el_wset (self->editline, EL_SETFP, 1, out); +} + static void input_el_buffer_switch (void *input, input_buffer_t input_buffer) { struct input_el *self = input; struct input_el_buffer *buffer = input_buffer; + if (!self->active) + return; if (self->current) input_el__save_buffer (self, self->current); - input_el__restore_buffer (self, buffer); - el_wset (self->editline, EL_HIST, history, buffer->history); self->current = buffer; + el_wset (self->editline, EL_HIST, history, buffer->history); + input_el__bottom (self); + input_el__restore_buffer (self, buffer); } static void @@ -1111,7 +1144,7 @@ input_el_on_tty_readable (void *input) if (!buf || count-- <= 0) return; - if (count == 0 && buf[0] == ('D' - 0x40) /* hardcoded VEOF in editline */) + if (count == 0 && buf[0] == ('D' - 0x40) /* hardcoded VEOF in el_wgets() */) { el_deletestr (self->editline, 1); input_el__redisplay (self); @@ -1131,6 +1164,7 @@ input_el_destroy (void *input) free (iter->help); ffi_closure_free (iter); } + fclose (self->null); free (self->prompt); free (self); } @@ -1144,6 +1178,7 @@ input_el_new (void) { struct input_el *self = xcalloc (1, sizeof *self); self->super.vtable = &input_el_vtable; + self->null = fopen ("/dev/null", "w"); return &self->super; } @@ -13908,13 +13943,10 @@ on_editline_return (EditLine *editline, int key) wchar_t *line = calloc (sizeof *info->buffer, len + 1); memcpy (line, info->buffer, sizeof *info->buffer * len); - // XXX: Editline seems to remember its position in history, - // so it's not going to work as you'd expect it to if (*line) { HistEventW ev; history_w (self->current->history, &ev, H_ENTER, line); - print_debug ("history: %d %ls", ev.num, ev.str); } free (line); @@ -13926,6 +13958,7 @@ on_editline_return (EditLine *editline, int key) el_cursor (editline, len - point); el_wdeletestr (editline, len); + input_el__bottom (self); return CC_REFRESH; }