degesch: more GNU Readline work
I'm not so sure anymore I will be able to achieve my goals with this library. It's really a terrible mess. A consistent and neatly formatted terrible mess.
This commit is contained in:
parent
3df841f088
commit
4a0c774e75
219
degesch.c
219
degesch.c
@ -187,6 +187,11 @@ struct buffer
|
||||
enum buffer_type type; ///< Type of the buffer
|
||||
char *name; ///< The name of the buffer
|
||||
|
||||
HISTORY_STATE *history; ///< Saved history state
|
||||
char *saved_line; ///< Saved line
|
||||
int saved_point; ///< Saved position in line
|
||||
int saved_mark; ///< Saved mark
|
||||
|
||||
// Buffer contents:
|
||||
|
||||
struct buffer_line *lines; ///< All lines in this buffer
|
||||
@ -215,6 +220,8 @@ static void
|
||||
buffer_destroy (struct buffer *self)
|
||||
{
|
||||
free (self->name);
|
||||
// Can't really free "history" here
|
||||
free (self->saved_line);
|
||||
free (self->topic);
|
||||
str_map_free (&self->nicks);
|
||||
free (self);
|
||||
@ -361,6 +368,8 @@ app_context_free (struct app_context *self)
|
||||
free (self->readline_prompt);
|
||||
}
|
||||
|
||||
static void refresh_prompt (struct app_context *ctx);
|
||||
|
||||
// --- Attributed output -------------------------------------------------------
|
||||
|
||||
static struct
|
||||
@ -417,12 +426,14 @@ struct app_readline_state
|
||||
{
|
||||
char *saved_line;
|
||||
int saved_point;
|
||||
int saved_mark;
|
||||
};
|
||||
|
||||
static void
|
||||
app_readline_hide (struct app_readline_state *state)
|
||||
{
|
||||
state->saved_point = rl_point;
|
||||
state->saved_point = rl_mark;
|
||||
state->saved_line = rl_copy_text (0, rl_end);
|
||||
rl_set_prompt ("");
|
||||
rl_replace_line ("", 0);
|
||||
@ -435,6 +446,7 @@ app_readline_restore (struct app_readline_state *state, const char *prompt)
|
||||
rl_set_prompt (prompt);
|
||||
rl_replace_line (state->saved_line, 0);
|
||||
rl_point = state->saved_point;
|
||||
rl_mark = state->saved_mark;
|
||||
rl_redisplay ();
|
||||
free (state->saved_line);
|
||||
}
|
||||
@ -677,6 +689,12 @@ buffer_line_display (struct app_context *ctx, struct buffer_line *line)
|
||||
char *object = iconv_xstrdup (ctx->term_from_utf8, line->object, -1, NULL);
|
||||
char *reason = iconv_xstrdup (ctx->term_from_utf8, line->reason, -1, NULL);
|
||||
|
||||
// TODO: colorize the output, note that we shouldn't put everything through
|
||||
// tputs but only the attribute strings. That might prove a bit
|
||||
// challenging. Maybe we could create a helper object to pust text
|
||||
// and formatting into. We could have a varargs function to make it a bit
|
||||
// more friendly, e.g. push(&x, ATTR_JOIN, "--> ", ATTR_RESET, who, NULL)
|
||||
|
||||
switch (line->type)
|
||||
{
|
||||
case BUFFER_LINE_PRIVMSG:
|
||||
@ -724,12 +742,15 @@ buffer_line_display (struct app_context *ctx, struct buffer_line *line)
|
||||
free (object);
|
||||
free (reason);
|
||||
|
||||
// TODO: hide readline if needed
|
||||
struct app_readline_state state;
|
||||
if (ctx->readline_prompt_shown)
|
||||
app_readline_hide (&state);
|
||||
|
||||
printf ("%s\n", text.str);
|
||||
str_free (&text);
|
||||
|
||||
// TODO: unhide readline if hidden
|
||||
if (ctx->readline_prompt_shown)
|
||||
app_readline_restore (&state, ctx->readline_prompt);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -773,7 +794,7 @@ buffer_add (struct app_context *ctx, struct buffer *buffer)
|
||||
str_map_set (&ctx->buffers_by_name, buffer->name, buffer);
|
||||
LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
|
||||
|
||||
// TODO: refresh the prompt?
|
||||
// TODO: refresh the prompt? Or caller?
|
||||
}
|
||||
|
||||
static void
|
||||
@ -781,6 +802,21 @@ buffer_remove (struct app_context *ctx, struct buffer *buffer)
|
||||
{
|
||||
hard_assert (buffer != ctx->current_buffer);
|
||||
|
||||
if (buffer->history)
|
||||
{
|
||||
// See buffer_activate() for why we need to do this BS
|
||||
rl_free_undo_list ();
|
||||
|
||||
// This is probably the only way we can free the history fully
|
||||
HISTORY_STATE *state = history_get_history_state ();
|
||||
|
||||
history_set_history_state (buffer->history);
|
||||
rl_clear_history ();
|
||||
|
||||
history_set_history_state (state);
|
||||
free (state);
|
||||
}
|
||||
|
||||
str_map_set (&ctx->buffers_by_name, buffer->name, NULL);
|
||||
LIST_UNLINK_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
|
||||
buffer_destroy (buffer);
|
||||
@ -792,22 +828,85 @@ buffer_remove (struct app_context *ctx, struct buffer *buffer)
|
||||
if (buffer == ctx->server_buffer)
|
||||
ctx->server_buffer = NULL;
|
||||
|
||||
// TODO: refresh the prompt?
|
||||
// TODO: refresh the prompt? Or caller?
|
||||
}
|
||||
|
||||
static void
|
||||
buffer_activate (struct app_context *ctx, struct buffer *buffer)
|
||||
{
|
||||
ctx->current_buffer = buffer;
|
||||
if (ctx->current_buffer == buffer)
|
||||
return;
|
||||
|
||||
print_status ("%s", buffer->name);
|
||||
|
||||
// That is, minus the buffer switch line and the readline prompt
|
||||
int to_display = ctx->lines - 2;
|
||||
// TODO: find the to_display-th line from the back
|
||||
// TODO: print all the lines in order
|
||||
int to_display = MAX (10, ctx->lines - 2);
|
||||
struct buffer_line *line = buffer->lines_tail;
|
||||
while (line && line->prev && --to_display > 0)
|
||||
line = line->prev;
|
||||
|
||||
// TODO: switch readline history stack
|
||||
// TODO: refresh the prompt?
|
||||
// Once we've found where we want to start with the backlog, print it
|
||||
for (; line; line = line->next)
|
||||
buffer_line_display (ctx, line);
|
||||
|
||||
// The following part shows you why it's not a good idea to use
|
||||
// GNU Readline for this kind of software. Or for anything else, really.
|
||||
|
||||
// There could possibly be occurences of the current undo list in some
|
||||
// history entry. We either need to free the undo list, or move it
|
||||
// somewhere else to load back later, as the buffer we're switching to
|
||||
// has its own history state.
|
||||
rl_free_undo_list ();
|
||||
|
||||
// Save this buffer's history so that it's independent for each buffer
|
||||
if (ctx->current_buffer)
|
||||
{
|
||||
ctx->current_buffer->history = history_get_history_state ();
|
||||
ctx->current_buffer->saved_line = rl_copy_text (0, rl_end);
|
||||
ctx->current_buffer->saved_point = rl_point;
|
||||
ctx->current_buffer->saved_mark = rl_mark;
|
||||
}
|
||||
else
|
||||
// Just throw it away; there should always be an active buffer however
|
||||
rl_clear_history ();
|
||||
|
||||
// Now at last we can switch the pointers
|
||||
ctx->current_buffer = buffer;
|
||||
|
||||
// Restore the target buffer's history
|
||||
if (buffer->history)
|
||||
{
|
||||
// history_get_history_state() just allocates a new HISTORY_STATE
|
||||
// and fills it with its current internal data. We don't need that
|
||||
// shell anymore after reviving it.
|
||||
history_set_history_state (buffer->history);
|
||||
free (buffer->history);
|
||||
buffer->history = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This should get us a clean history while keeping the flags.
|
||||
// Note that we've either saved the previous history entries, or we've
|
||||
// cleared them altogether, so there should be nothing to leak.
|
||||
HISTORY_STATE *state = history_get_history_state ();
|
||||
state->offset = state->length = state->size = 0;
|
||||
history_set_history_state (state);
|
||||
free (state);
|
||||
}
|
||||
|
||||
// Try to restore the target buffer's readline state
|
||||
if (buffer->saved_line)
|
||||
{
|
||||
rl_replace_line (buffer->saved_line, 0);
|
||||
rl_point = buffer->saved_point;
|
||||
rl_mark = buffer->saved_mark;
|
||||
free (buffer->saved_line);
|
||||
|
||||
if (ctx->readline_prompt_shown)
|
||||
rl_redisplay ();
|
||||
}
|
||||
|
||||
// TODO: refresh the prompt? Or caller?
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1099,6 +1198,8 @@ irc_establish_connection (struct app_context *ctx,
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- More readline funky stuff -----------------------------------------------
|
||||
|
||||
static void
|
||||
refresh_prompt (struct app_context *ctx)
|
||||
{
|
||||
@ -1140,10 +1241,69 @@ refresh_prompt (struct app_context *ctx)
|
||||
}
|
||||
str_free (&prompt);
|
||||
|
||||
// FIXME: when the program hasn't displayed the prompt yet, is this okay?
|
||||
// We need to be somehow able to initialize it
|
||||
rl_set_prompt (ctx->readline_prompt);
|
||||
if (ctx->readline_prompt_shown)
|
||||
rl_redisplay ();
|
||||
}
|
||||
|
||||
static int
|
||||
on_readline_goto_buffer (int count, int key)
|
||||
{
|
||||
(void) count;
|
||||
|
||||
if (!(key & 0x80))
|
||||
return 0;
|
||||
|
||||
int n = (key & 0x7F) - '0';
|
||||
if (n < 0 || n > 9)
|
||||
return 0;
|
||||
|
||||
// TODO: switch to the n-th buffer
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
on_readline_previous_buffer (int count, int key)
|
||||
{
|
||||
(void) key;
|
||||
|
||||
// TODO: switch "count" times to the previous buffer
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
on_readline_next_buffer (int count, int key)
|
||||
{
|
||||
(void) key;
|
||||
|
||||
// TODO: switch "count" times to the next buffer
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
init_readline (void)
|
||||
{
|
||||
// TODO: maybe use rl_make_bare_keymap() and start from there;
|
||||
// our dear user could potentionally rig things up in a way that might
|
||||
// result in some funny unspecified behaviour
|
||||
|
||||
rl_add_defun ("previous-buffer", on_readline_previous_buffer, -1);
|
||||
rl_add_defun ("next-buffer", on_readline_next_buffer, -1);
|
||||
|
||||
// Redefine M-0 through M-9 to switch buffers
|
||||
for (int i = 0; i <= 9; i++)
|
||||
rl_bind_key (0x80 /* this is the Meta modifier for Readline */
|
||||
| ('0' + i), on_readline_goto_buffer);
|
||||
|
||||
rl_bind_keyseq ("C-p", rl_named_function ("previous-buffer"));
|
||||
rl_bind_keyseq ("C-n", rl_named_function ("next-buffer"));
|
||||
rl_bind_keyseq ("M-p", rl_named_function ("previous-history"));
|
||||
rl_bind_keyseq ("M-n", rl_named_function ("next-history"));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --- Input handling ----------------------------------------------------------
|
||||
|
||||
static void
|
||||
@ -1268,24 +1428,6 @@ fail:
|
||||
free (input);
|
||||
}
|
||||
|
||||
static int
|
||||
on_readline_previous_buffer (int count, int key)
|
||||
{
|
||||
(void) key;
|
||||
|
||||
// TODO: switch to the previous buffer
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
on_readline_next_buffer (int count, int key)
|
||||
{
|
||||
(void) key;
|
||||
|
||||
// TODO: switch to the next buffer
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --- Supporting code (continued) ---------------------------------------------
|
||||
|
||||
enum irc_read_result
|
||||
@ -1898,6 +2040,7 @@ main (int argc, char *argv[])
|
||||
atexit (ERR_free_strings);
|
||||
|
||||
using_history ();
|
||||
// This can cause memory leaks, or maybe even a segfault. Funny, eh?
|
||||
stifle_history (HISTORY_LIMIT);
|
||||
|
||||
setup_signal_handlers ();
|
||||
@ -1905,6 +2048,7 @@ main (int argc, char *argv[])
|
||||
init_colors (&ctx);
|
||||
init_poller_events (&ctx);
|
||||
init_buffers (&ctx);
|
||||
ctx.current_buffer = ctx.global_buffer;
|
||||
refresh_prompt (&ctx);
|
||||
|
||||
// TODO: connect asynchronously (first step towards multiple servers)
|
||||
@ -1917,22 +2061,7 @@ main (int argc, char *argv[])
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// TODO: maybe use rl_make_bare_keymap() and start from there
|
||||
|
||||
// XXX: Since readline() installs a set of default key bindings the first
|
||||
// time it is called, there is always the danger that a custom binding
|
||||
// installed before the first call to readline() will be overridden.
|
||||
// An alternate mechanism is to install custom key bindings in an
|
||||
// initialization function assigned to the rl_startup_hook variable.
|
||||
rl_add_defun ("previous-buffer", on_readline_previous_buffer, -1);
|
||||
rl_add_defun ("next-buffer", on_readline_next_buffer, -1);
|
||||
|
||||
// TODO: redefine M-0 through M-9 to switch buffers
|
||||
rl_bind_keyseq ("C-p", rl_named_function ("previous-buffer"));
|
||||
rl_bind_keyseq ("C-n", rl_named_function ("next-buffer"));
|
||||
rl_bind_keyseq ("M-p", rl_named_function ("previous-history"));
|
||||
rl_bind_keyseq ("M-n", rl_named_function ("next-history"));
|
||||
|
||||
rl_startup_hook = init_readline;
|
||||
rl_catch_sigwinch = false;
|
||||
rl_callback_handler_install (ctx.readline_prompt, on_readline_input);
|
||||
rl_get_screen_size (&ctx.lines, &ctx.columns);
|
||||
|
Loading…
Reference in New Issue
Block a user