degesch: add support for libedit
Just another kind of evil.
This commit is contained in:
parent
53894e3909
commit
a5a1079a9c
|
@ -1,6 +1,10 @@
|
|||
project (uirc3 C)
|
||||
cmake_minimum_required (VERSION 2.8.5)
|
||||
|
||||
# Options
|
||||
option (WANT_READLINE "Use GNU Readline for the UI (better)" ON)
|
||||
option (WANT_EDITLINE "Use BSD libedit for the UI" OFF)
|
||||
|
||||
# Moar warnings
|
||||
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
|
||||
# -Wunused-function is pretty annoying here, as everything is static
|
||||
|
@ -38,7 +42,25 @@ else (CURSES_FOUND)
|
|||
message (SEND_ERROR "Curses not found")
|
||||
endif (ncursesw_FOUND)
|
||||
|
||||
if ((WANT_READLINE AND WANT_EDITLINE) OR (NOT WANT_READLINE AND NOT WANT_EDITLINE))
|
||||
message (SEND_ERROR "You have to choose either GNU Readline or libedit")
|
||||
elseif (WANT_READLINE)
|
||||
list (APPEND project_libraries readline)
|
||||
elseif (WANT_EDITLINE)
|
||||
pkg_check_modules (libedit REQUIRED libedit)
|
||||
list (APPEND project_libraries ${libedit_LIBRARIES})
|
||||
include_directories (${libedit_INCLUDE_DIRS})
|
||||
endif ((WANT_READLINE AND WANT_EDITLINE) OR (NOT WANT_READLINE AND NOT WANT_EDITLINE))
|
||||
|
||||
# Generate a configuration file
|
||||
if (WANT_READLINE)
|
||||
set (HAVE_READLINE 1)
|
||||
endif (WANT_READLINE)
|
||||
|
||||
if (WANT_EDITLINE)
|
||||
set (HAVE_EDITLINE 1)
|
||||
endif (WANT_EDITLINE)
|
||||
|
||||
include (GNUInstallDirs)
|
||||
set (plugin_dir ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME})
|
||||
configure_file (${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config.h)
|
||||
|
@ -62,7 +84,7 @@ target_link_libraries (zyklonb ${project_libraries})
|
|||
|
||||
add_executable (degesch degesch.c kike-replies.c
|
||||
${common_sources} ${common_headers})
|
||||
target_link_libraries (degesch ${project_libraries} readline)
|
||||
target_link_libraries (degesch ${project_libraries})
|
||||
|
||||
add_executable (kike kike.c kike-replies.c ${common_sources} ${common_headers})
|
||||
target_link_libraries (kike ${project_libraries})
|
||||
|
|
5
README
5
README
|
@ -50,14 +50,15 @@ Notable features:
|
|||
Building
|
||||
--------
|
||||
Build dependencies: CMake, pkg-config, help2man, awk, sh, liberty (included)
|
||||
Runtime dependencies: openssl, curses (degesch), readline (degesch)
|
||||
Runtime dependencies: openssl, curses (degesch), readline or libedit (degesch)
|
||||
|
||||
$ git clone https://github.com/pjanouch/uirc3.git
|
||||
$ git submodule init
|
||||
$ git submodule update
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug
|
||||
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DWANT_READLINE=ON -DWANT_LIBEDIT=OFF
|
||||
$ make
|
||||
|
||||
To install the application, you can do either the usual:
|
||||
|
|
|
@ -4,4 +4,7 @@
|
|||
#define PROGRAM_VERSION "${project_VERSION}"
|
||||
#define PLUGIN_DIR "${CMAKE_INSTALL_PREFIX}/${plugin_dir}"
|
||||
|
||||
#cmakedefine HAVE_READLINE
|
||||
#cmakedefine HAVE_EDITLINE
|
||||
|
||||
#endif // ! CONFIG_H
|
||||
|
|
515
degesch.c
515
degesch.c
|
@ -69,32 +69,55 @@ enum
|
|||
#undef lines
|
||||
#undef columns
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#endif // HAVE_READLINE
|
||||
|
||||
#ifdef HAVE_EDITLINE
|
||||
#include <histedit.h>
|
||||
#endif // HAVE_EDITLINE
|
||||
|
||||
// --- User interface ----------------------------------------------------------
|
||||
|
||||
// Currently provided by GNU Readline. A libedit backend is also possible.
|
||||
// I'm not sure which one of these backends is worse: whether it's GNU Readline
|
||||
// or BSD Editline. They both have their own annoying problems.
|
||||
|
||||
struct input_buffer
|
||||
{
|
||||
#ifdef HAVE_READLINE
|
||||
HISTORY_STATE *history; ///< Saved history state
|
||||
char *saved_line; ///< Saved line content
|
||||
int saved_point; ///< Saved cursor position
|
||||
int saved_mark; ///< Saved mark
|
||||
#elif defined HAVE_EDITLINE
|
||||
HistoryW *history; ///< The history object
|
||||
wchar_t *saved_line; ///< Saved line content
|
||||
int saved_len; ///< Length of the saved line
|
||||
#endif // HAVE_EDITLINE
|
||||
int saved_point; ///< Saved cursor position
|
||||
};
|
||||
|
||||
static struct input_buffer *
|
||||
input_buffer_new (void)
|
||||
{
|
||||
struct input_buffer *self = xcalloc (1, sizeof *self);
|
||||
#ifdef HAVE_EDITLINE
|
||||
self->history = history_winit ();
|
||||
|
||||
HistEventW ev;
|
||||
history_w (self->history, &ev, H_SETSIZE, HISTORY_LIMIT);
|
||||
#endif // HAVE_EDITLINE
|
||||
return self;
|
||||
}
|
||||
|
||||
static void
|
||||
input_buffer_destroy (struct input_buffer *self)
|
||||
{
|
||||
#ifdef HAVE_READLINE
|
||||
// Can't really free "history" from here
|
||||
#elif defined HAVE_EDITLINE
|
||||
history_wend (self->history);
|
||||
#endif // HAVE_EDITLINE
|
||||
free (self->saved_line);
|
||||
free (self);
|
||||
}
|
||||
|
@ -103,9 +126,15 @@ struct input
|
|||
{
|
||||
bool active; ///< Are we a thing?
|
||||
|
||||
#if defined HAVE_READLINE
|
||||
char *saved_line; ///< Saved line content
|
||||
int saved_point; ///< Saved cursor position
|
||||
int saved_mark; ///< Saved mark
|
||||
#elif defined HAVE_EDITLINE
|
||||
EditLine *editline; ///< The EditLine object
|
||||
char *(*saved_prompt) (EditLine *); ///< Saved prompt function
|
||||
char saved_char; ///< Saved char for the prompt
|
||||
#endif // HAVE_EDITLINE
|
||||
|
||||
char *prompt; ///< The prompt we use
|
||||
int prompt_shown; ///< Whether the prompt is shown now
|
||||
|
@ -122,12 +151,19 @@ input_init (struct input *self)
|
|||
static void
|
||||
input_free (struct input *self)
|
||||
{
|
||||
#ifdef HAVE_READLINE
|
||||
free (self->saved_line);
|
||||
#endif // HAVE_READLINE
|
||||
free (self->prompt);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
|
||||
#define INPUT_START_IGNORE RL_PROMPT_START_IGNORE
|
||||
#define INPUT_END_IGNORE RL_PROMPT_END_IGNORE
|
||||
|
||||
#define input_ding(self) rl_ding ()
|
||||
|
||||
static void
|
||||
|
@ -181,8 +217,14 @@ static int app_readline_init (void);
|
|||
static void on_readline_input (char *line);
|
||||
|
||||
static void
|
||||
input_start (struct input *self)
|
||||
input_start (struct input *self, const char *program_name)
|
||||
{
|
||||
(void) program_name;
|
||||
|
||||
using_history ();
|
||||
// This can cause memory leaks, or maybe even a segfault. Funny, eh?
|
||||
stifle_history (HISTORY_LIMIT);
|
||||
|
||||
rl_startup_hook = app_readline_init;
|
||||
rl_catch_sigwinch = false;
|
||||
rl_callback_handler_install (self->prompt, on_readline_input);
|
||||
|
@ -203,53 +245,6 @@ input_stop (struct input *self)
|
|||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static void
|
||||
input_save (struct input *self)
|
||||
{
|
||||
hard_assert (!self->saved_line);
|
||||
|
||||
self->saved_point = rl_point;
|
||||
self->saved_mark = rl_mark;
|
||||
self->saved_line = rl_copy_text (0, rl_end);
|
||||
}
|
||||
|
||||
static void
|
||||
input_restore (struct input *self)
|
||||
{
|
||||
hard_assert (self->saved_line);
|
||||
|
||||
rl_set_prompt (self->prompt);
|
||||
rl_replace_line (self->saved_line, 0);
|
||||
rl_point = self->saved_point;
|
||||
rl_mark = self->saved_mark;
|
||||
free (self->saved_line);
|
||||
self->saved_line = NULL;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static void
|
||||
input_hide (struct input *self)
|
||||
{
|
||||
if (!self->active || self->prompt_shown-- < 1)
|
||||
return;
|
||||
|
||||
input_save (self);
|
||||
input_erase (self);
|
||||
}
|
||||
|
||||
static void
|
||||
input_show (struct input *self)
|
||||
{
|
||||
if (!self->active || ++self->prompt_shown < 1)
|
||||
return;
|
||||
|
||||
input_restore (self);
|
||||
rl_redisplay ();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
// 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.
|
||||
|
||||
|
@ -337,7 +332,7 @@ input_destroy_buffer (struct input *self, struct input_buffer *buffer)
|
|||
#if RL_READLINE_VERSION >= 0x0603
|
||||
if (buffer->history)
|
||||
{
|
||||
// See buffer_activate() for why we need to do this BS
|
||||
// See input_switch_buffer() for why we need to do this BS
|
||||
rl_free_undo_list ();
|
||||
|
||||
// This is probably the only way we can free the history fully
|
||||
|
@ -355,6 +350,275 @@ input_destroy_buffer (struct input *self, struct input_buffer *buffer)
|
|||
input_buffer_destroy (buffer);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static void
|
||||
input_save (struct input *self)
|
||||
{
|
||||
hard_assert (!self->saved_line);
|
||||
|
||||
self->saved_point = rl_point;
|
||||
self->saved_mark = rl_mark;
|
||||
self->saved_line = rl_copy_text (0, rl_end);
|
||||
}
|
||||
|
||||
static void
|
||||
input_restore (struct input *self)
|
||||
{
|
||||
hard_assert (self->saved_line);
|
||||
|
||||
rl_set_prompt (self->prompt);
|
||||
rl_replace_line (self->saved_line, 0);
|
||||
rl_point = self->saved_point;
|
||||
rl_mark = self->saved_mark;
|
||||
free (self->saved_line);
|
||||
self->saved_line = NULL;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static void
|
||||
input_hide (struct input *self)
|
||||
{
|
||||
if (!self->active || self->prompt_shown-- < 1)
|
||||
return;
|
||||
|
||||
input_save (self);
|
||||
input_erase (self);
|
||||
}
|
||||
|
||||
static void
|
||||
input_show (struct input *self)
|
||||
{
|
||||
if (!self->active || ++self->prompt_shown < 1)
|
||||
return;
|
||||
|
||||
input_restore (self);
|
||||
rl_redisplay ();
|
||||
}
|
||||
|
||||
#endif // HAVE_READLINE
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
#ifdef HAVE_EDITLINE
|
||||
|
||||
#define INPUT_START_IGNORE '\x01'
|
||||
#define INPUT_END_IGNORE '\x01'
|
||||
|
||||
static void app_editline_init (struct input *self);
|
||||
static void on_editline_input (struct input *self, char *line);
|
||||
|
||||
static void
|
||||
input_ding (struct input *self)
|
||||
{
|
||||
(void) self;
|
||||
|
||||
// XXX: this isn't probably very portable
|
||||
putc ('\a', stdout);
|
||||
}
|
||||
|
||||
static void
|
||||
input_on_terminal_resized (struct input *self)
|
||||
{
|
||||
el_resize (self->editline);
|
||||
}
|
||||
|
||||
static void
|
||||
input_redisplay (struct input *self)
|
||||
{
|
||||
// See rl_redisplay()
|
||||
// The character is VREPRINT (usually C-r)
|
||||
// TODO: read it from terminal info
|
||||
// XXX: could we potentially break UTF-8 with this?
|
||||
char x[] = { ('R' - 'A' + 1), 0 };
|
||||
el_push (self->editline, x);
|
||||
|
||||
// We have to do this or it gets stuck and nothing is done
|
||||
(void) el_gets (self->editline, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
input_set_prompt (struct input *self, char *prompt)
|
||||
{
|
||||
free (self->prompt);
|
||||
self->prompt = prompt;
|
||||
|
||||
if (self->prompt_shown)
|
||||
input_redisplay (self);
|
||||
}
|
||||
|
||||
static char *
|
||||
input_make_prompt (EditLine *editline)
|
||||
{
|
||||
struct input *self;
|
||||
el_get (editline, EL_CLIENTDATA, &self);
|
||||
return self->prompt;
|
||||
}
|
||||
|
||||
static char *
|
||||
input_make_empty_prompt (EditLine *editline)
|
||||
{
|
||||
(void) editline;
|
||||
return "";
|
||||
}
|
||||
|
||||
static void
|
||||
input_erase (struct input *self)
|
||||
{
|
||||
const LineInfoW *info = el_wline (self->editline);
|
||||
int len = info->lastchar - info->buffer;
|
||||
int point = info->cursor - info->buffer;
|
||||
el_cursor (self->editline, len - point);
|
||||
el_wdeletestr (self->editline, len);
|
||||
|
||||
// XXX: this doesn't seem to save the escape character
|
||||
el_get (self->editline, EL_PROMPT, &self->saved_prompt, &self->saved_char);
|
||||
el_set (self->editline, EL_PROMPT, input_make_empty_prompt);
|
||||
input_redisplay (self);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static void
|
||||
input_start (struct input *self, const char *program_name)
|
||||
{
|
||||
self->editline = el_init (program_name, stdin, stdout, stderr);
|
||||
el_set (self->editline, EL_CLIENTDATA, self);
|
||||
el_set (self->editline, EL_PROMPT_ESC,
|
||||
input_make_prompt, INPUT_START_IGNORE);
|
||||
el_set (self->editline, EL_SIGNAL, false);
|
||||
el_set (self->editline, EL_UNBUFFERED, true);
|
||||
el_set (self->editline, EL_EDITOR, "emacs");
|
||||
|
||||
app_editline_init (self);
|
||||
self->prompt_shown = 1;
|
||||
self->active = true;
|
||||
}
|
||||
|
||||
static void
|
||||
input_stop (struct input *self)
|
||||
{
|
||||
if (self->prompt_shown > 0)
|
||||
input_erase (self);
|
||||
|
||||
el_end (self->editline);
|
||||
self->editline = NULL;
|
||||
self->active = false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static void
|
||||
input_save_buffer (struct input *self, struct input_buffer *buffer)
|
||||
{
|
||||
const LineInfoW *info = el_wline (self->editline);
|
||||
int len = info->lastchar - info->buffer;
|
||||
int point = info->cursor - info->buffer;
|
||||
|
||||
wchar_t *line = calloc (sizeof *info->buffer, len + 1);
|
||||
memcpy (line, info->buffer, sizeof *info->buffer * len);
|
||||
el_cursor (self->editline, len - point);
|
||||
el_wdeletestr (self->editline, len);
|
||||
|
||||
buffer->saved_line = line;
|
||||
buffer->saved_point = point;
|
||||
buffer->saved_len = len;
|
||||
}
|
||||
|
||||
static void
|
||||
input_restore_buffer (struct input *self, struct input_buffer *buffer)
|
||||
{
|
||||
if (buffer->saved_line)
|
||||
{
|
||||
el_winsertstr (self->editline, buffer->saved_line);
|
||||
el_cursor (self->editline,
|
||||
-(buffer->saved_len - buffer->saved_point));
|
||||
free (buffer->saved_line);
|
||||
buffer->saved_line = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
input_switch_buffer (struct input *self, struct input_buffer *buffer)
|
||||
{
|
||||
if (self->current)
|
||||
input_save_buffer (self, self->current);
|
||||
|
||||
input_restore_buffer (self, buffer);
|
||||
el_wset (self->editline, EL_HIST, history, buffer->history);
|
||||
self->current = buffer;
|
||||
}
|
||||
|
||||
static void
|
||||
input_destroy_buffer (struct input *self, struct input_buffer *buffer)
|
||||
{
|
||||
(void) self;
|
||||
input_buffer_destroy (buffer);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static void
|
||||
input_save (struct input *self)
|
||||
{
|
||||
input_save_buffer (self, self->current);
|
||||
}
|
||||
|
||||
static void
|
||||
input_restore (struct input *self)
|
||||
{
|
||||
input_restore_buffer (self, self->current);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static void
|
||||
input_hide (struct input *self)
|
||||
{
|
||||
if (!self->active || self->prompt_shown-- < 1)
|
||||
return;
|
||||
|
||||
input_save (self);
|
||||
input_erase (self);
|
||||
}
|
||||
|
||||
static void
|
||||
input_show (struct input *self)
|
||||
{
|
||||
if (!self->active || ++self->prompt_shown < 1)
|
||||
return;
|
||||
|
||||
input_restore (self);
|
||||
// Would have used "saved_char" but it doesn't seem to work.
|
||||
// And it doesn't even when it does anyway (it seems to just strip it).
|
||||
el_set (self->editline,
|
||||
EL_PROMPT_ESC, input_make_prompt, INPUT_START_IGNORE);
|
||||
input_redisplay (self);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static void
|
||||
input_on_readable (struct input *self)
|
||||
{
|
||||
// We bind the return key to process it how we need to
|
||||
|
||||
// el_gets() with EL_UNBUFFERED doesn't work with UTF-8,
|
||||
// we must use the wide-character interface
|
||||
int count = 0;
|
||||
const wchar_t *buf = el_wgets (self->editline, &count);
|
||||
if (!buf || count-- <= 0)
|
||||
return;
|
||||
|
||||
// The character is VEOF (usually C-d)
|
||||
// TODO: read it from terminal info
|
||||
if (count == 0 && buf[0] == ('D' - 'A' + 1))
|
||||
input_ding (self);
|
||||
}
|
||||
|
||||
#endif // HAVE_EDITLINE
|
||||
|
||||
// --- Application data --------------------------------------------------------
|
||||
|
||||
// All text stored in our data structures is encoded in UTF-8.
|
||||
|
@ -2560,18 +2824,18 @@ refresh_prompt (struct app_context *ctx)
|
|||
make_prompt (ctx, &prompt);
|
||||
str_append_c (&prompt, ' ');
|
||||
|
||||
if (!have_attributes)
|
||||
input_set_prompt (&ctx->input, xstrdup (prompt.str));
|
||||
else
|
||||
if (have_attributes)
|
||||
{
|
||||
// XXX: to be completely correct, we should use tputs, but we cannot
|
||||
input_set_prompt (&ctx->input, xstrdup_printf ("%c%s%c%s%c%s%c",
|
||||
RL_PROMPT_START_IGNORE, ctx->attrs[ATTR_PROMPT],
|
||||
RL_PROMPT_END_IGNORE,
|
||||
INPUT_START_IGNORE, ctx->attrs[ATTR_PROMPT],
|
||||
INPUT_END_IGNORE,
|
||||
prompt.str,
|
||||
RL_PROMPT_START_IGNORE, ctx->attrs[ATTR_RESET],
|
||||
RL_PROMPT_END_IGNORE));
|
||||
INPUT_START_IGNORE, ctx->attrs[ATTR_RESET],
|
||||
INPUT_END_IGNORE));
|
||||
}
|
||||
else
|
||||
input_set_prompt (&ctx->input, xstrdup (prompt.str));
|
||||
str_free (&prompt);
|
||||
}
|
||||
|
||||
|
@ -4738,6 +5002,8 @@ irc_connect (struct server *s, bool *should_retry, struct error **e)
|
|||
|
||||
// --- User interface actions --------------------------------------------------
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
|
||||
static int
|
||||
on_readline_goto_buffer (int count, int key)
|
||||
{
|
||||
|
@ -4755,7 +5021,7 @@ on_readline_goto_buffer (int count, int key)
|
|||
if (ctx->last_buffer && buffer_get_index (ctx, ctx->current_buffer) == n)
|
||||
// Fast switching between two buffers
|
||||
buffer_activate (ctx, ctx->last_buffer);
|
||||
else if (!buffer_goto (ctx, n == 0 ? 10 : n))
|
||||
else if (!buffer_goto (ctx, n))
|
||||
input_ding (self);
|
||||
return 0;
|
||||
}
|
||||
|
@ -4766,8 +5032,7 @@ on_readline_previous_buffer (int count, int key)
|
|||
(void) key;
|
||||
|
||||
struct app_context *ctx = g_ctx;
|
||||
if (ctx->current_buffer)
|
||||
buffer_activate (ctx, buffer_previous (ctx, count));
|
||||
buffer_activate (ctx, buffer_previous (ctx, count));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4777,8 +5042,7 @@ on_readline_next_buffer (int count, int key)
|
|||
(void) key;
|
||||
|
||||
struct app_context *ctx = g_ctx;
|
||||
if (ctx->current_buffer)
|
||||
buffer_activate (ctx, buffer_next (ctx, count));
|
||||
buffer_activate (ctx, buffer_next (ctx, count));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4862,6 +5126,125 @@ app_readline_init (void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#endif // HAVE_READLINE
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
#ifdef HAVE_EDITLINE
|
||||
|
||||
static unsigned char
|
||||
on_editline_goto_buffer (EditLine *editline, int key)
|
||||
{
|
||||
(void) editline;
|
||||
|
||||
int n = key - '0';
|
||||
if (n < 0 || n > 9)
|
||||
return CC_ERROR;
|
||||
|
||||
// There's no buffer zero
|
||||
if (n == 0)
|
||||
n = 10;
|
||||
|
||||
struct app_context *ctx = g_ctx;
|
||||
if (ctx->last_buffer && buffer_get_index (ctx, ctx->current_buffer) == n)
|
||||
// Fast switching between two buffers
|
||||
buffer_activate (ctx, ctx->last_buffer);
|
||||
else if (!buffer_goto (ctx, n))
|
||||
return CC_ERROR;
|
||||
return CC_NORM;
|
||||
}
|
||||
|
||||
static unsigned char
|
||||
on_editline_previous_buffer (EditLine *editline, int key)
|
||||
{
|
||||
(void) editline;
|
||||
(void) key;
|
||||
|
||||
struct app_context *ctx = g_ctx;
|
||||
buffer_activate (ctx, buffer_previous (ctx, 1));
|
||||
return CC_NORM;
|
||||
}
|
||||
|
||||
static unsigned char
|
||||
on_editline_next_buffer (EditLine *editline, int key)
|
||||
{
|
||||
(void) editline;
|
||||
(void) key;
|
||||
|
||||
struct app_context *ctx = g_ctx;
|
||||
buffer_activate (ctx, buffer_next (ctx, 1));
|
||||
return CC_NORM;
|
||||
}
|
||||
|
||||
static unsigned char
|
||||
on_editline_return (EditLine *editline, int key)
|
||||
{
|
||||
(void) key;
|
||||
struct input *self = &g_ctx->input;
|
||||
|
||||
const LineInfoW *info = el_wline (editline);
|
||||
int len = info->lastchar - info->buffer;
|
||||
int point = info->cursor - info->buffer;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Convert it to multibyte to reflect the Readline interface
|
||||
size_t needed = wcstombs (NULL, line, 0) + 1;
|
||||
char converted[needed];
|
||||
if (!needed || wcstombs (converted, line, needed) == (size_t) -1)
|
||||
print_error ("encoding conversion failed");
|
||||
else
|
||||
process_input (g_ctx, converted);
|
||||
|
||||
free (line);
|
||||
|
||||
el_cursor (editline, len - point);
|
||||
el_wdeletestr (editline, len);
|
||||
return CC_REFRESH;
|
||||
}
|
||||
|
||||
static void
|
||||
app_editline_init (struct input *self)
|
||||
{
|
||||
el_set (self->editline, EL_ADDFN, "goto-buffer",
|
||||
"Go to buffer", on_editline_goto_buffer);
|
||||
el_set (self->editline, EL_ADDFN, "previous-buffer",
|
||||
"Previous buffer", on_editline_previous_buffer);
|
||||
el_set (self->editline, EL_ADDFN, "next-buffer",
|
||||
"Next buffer", on_editline_next_buffer);
|
||||
|
||||
// Redefine M-0 through M-9 to switch buffers
|
||||
for (size_t i = 0; i < 10; i++)
|
||||
{
|
||||
char keyseq[] = { 'M', '-', '0' + i, 0 };
|
||||
el_set (self->editline, EL_BIND, keyseq, "goto-buffer", NULL);
|
||||
}
|
||||
|
||||
el_set (self->editline, EL_BIND, "M-p", "ed-prev-history", NULL);
|
||||
el_set (self->editline, EL_BIND, "M-n", "ed-next-history", NULL);
|
||||
el_set (self->editline, EL_BIND, "^P", "previous-buffer", NULL);
|
||||
el_set (self->editline, EL_BIND, "^N", "next-buffer", NULL);
|
||||
|
||||
// Source the user's defaults file
|
||||
el_source (self->editline, NULL);
|
||||
|
||||
el_set (self->editline, EL_ADDFN, "send-line",
|
||||
"Send line", on_editline_return);
|
||||
el_set (self->editline, EL_BIND, "\n", "send-line", NULL);
|
||||
}
|
||||
|
||||
#endif // HAVE_EDITLINE
|
||||
|
||||
// --- I/O event handlers ------------------------------------------------------
|
||||
|
||||
static void
|
||||
|
@ -5095,10 +5478,6 @@ main (int argc, char *argv[])
|
|||
SSL_load_error_strings ();
|
||||
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 ();
|
||||
register_config_modules (&ctx);
|
||||
load_configuration (&ctx);
|
||||
|
@ -5109,7 +5488,7 @@ main (int argc, char *argv[])
|
|||
buffer_activate (&ctx, ctx.server.buffer);
|
||||
|
||||
refresh_prompt (&ctx);
|
||||
input_start (&ctx.input);
|
||||
input_start (&ctx.input, argv[0]);
|
||||
|
||||
// Connect to the server ASAP
|
||||
poller_timer_set (&ctx.server.reconnect_tmr, 0);
|
||||
|
|
Loading…
Reference in New Issue