Compare commits
4 Commits
2fe3b95ecd
...
2336340ad8
Author | SHA1 | Date |
---|---|---|
Přemysl Eric Janouch | 2336340ad8 | |
Přemysl Eric Janouch | 8f5dec0456 | |
Přemysl Eric Janouch | 3dc6ee9a5b | |
Přemysl Eric Janouch | 821ce04915 |
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required (VERSION 3.0)
|
cmake_minimum_required (VERSION 3.0)
|
||||||
project (uirc3 VERSION 1.0.0 LANGUAGES C)
|
project (uirc3 VERSION 1.1.0 LANGUAGES C)
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
option (WANT_READLINE "Use GNU Readline for the UI (better)" ON)
|
option (WANT_READLINE "Use GNU Readline for the UI (better)" ON)
|
||||||
|
|
9
NEWS
9
NEWS
|
@ -1,3 +1,12 @@
|
||||||
|
1.1.0 (2020-10-31) "What Do You Mean By 'This Isn't Germany'?"
|
||||||
|
|
||||||
|
* degesch: made fancy-prompt.lua work with libedit
|
||||||
|
|
||||||
|
* kike: fixed a regression with an unspecified "bind_host"
|
||||||
|
|
||||||
|
* Miscellaneous minor improvements
|
||||||
|
|
||||||
|
|
||||||
1.0.0 (2020-10-29) "We're Finally There!"
|
1.0.0 (2020-10-29) "We're Finally There!"
|
||||||
|
|
||||||
* Coming with real manual pages instead of help2man-generated stubs
|
* Coming with real manual pages instead of help2man-generated stubs
|
||||||
|
|
149
degesch.c
149
degesch.c
|
@ -1591,12 +1591,14 @@ static struct ispect_field g_buffer_ispect[] =
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct buffer *
|
static struct buffer *
|
||||||
buffer_new (struct input *input)
|
buffer_new (struct input *input, enum buffer_type type, char *name)
|
||||||
{
|
{
|
||||||
struct buffer *self = xcalloc (1, sizeof *self);
|
struct buffer *self = xcalloc (1, sizeof *self);
|
||||||
self->ref_count = 1;
|
self->ref_count = 1;
|
||||||
self->input = input;
|
self->input = input;
|
||||||
self->input_data = CALL (input, buffer_new);
|
self->input_data = CALL (input, buffer_new);
|
||||||
|
self->type = type;
|
||||||
|
self->name = name;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4446,9 +4448,8 @@ buffer_remove_safe (struct app_context *ctx, struct buffer *buffer)
|
||||||
static void
|
static void
|
||||||
init_global_buffer (struct app_context *ctx)
|
init_global_buffer (struct app_context *ctx)
|
||||||
{
|
{
|
||||||
struct buffer *global = ctx->global_buffer = buffer_new (ctx->input);
|
struct buffer *global = ctx->global_buffer =
|
||||||
global->type = BUFFER_GLOBAL;
|
buffer_new (ctx->input, BUFFER_GLOBAL, xstrdup (PROGRAM_NAME));
|
||||||
global->name = xstrdup (PROGRAM_NAME);
|
|
||||||
|
|
||||||
buffer_add (ctx, global);
|
buffer_add (ctx, global);
|
||||||
buffer_activate (ctx, global);
|
buffer_activate (ctx, global);
|
||||||
|
@ -4456,6 +4457,19 @@ init_global_buffer (struct app_context *ctx)
|
||||||
|
|
||||||
// --- Users, channels ---------------------------------------------------------
|
// --- Users, channels ---------------------------------------------------------
|
||||||
|
|
||||||
|
static char *
|
||||||
|
irc_make_buffer_name (struct server *s, const char *target)
|
||||||
|
{
|
||||||
|
if (!target)
|
||||||
|
return xstrdup (s->name);
|
||||||
|
|
||||||
|
// XXX: this may be able to trigger the uniqueness assertion with non-UTF-8
|
||||||
|
char *target_utf8 = irc_to_utf8 (target);
|
||||||
|
char *result = xstrdup_printf ("%s.%s", s->name, target_utf8);
|
||||||
|
free (target_utf8);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
irc_user_on_destroy (void *object, void *user_data)
|
irc_user_on_destroy (void *object, void *user_data)
|
||||||
{
|
{
|
||||||
|
@ -4495,10 +4509,8 @@ irc_get_or_make_user_buffer (struct server *s, const char *nickname)
|
||||||
struct user *user = irc_get_or_make_user (s, nickname);
|
struct user *user = irc_get_or_make_user (s, nickname);
|
||||||
|
|
||||||
// Open a new buffer for the user
|
// Open a new buffer for the user
|
||||||
buffer = buffer_new (s->ctx->input);
|
buffer = buffer_new (s->ctx->input,
|
||||||
buffer->type = BUFFER_PM;
|
BUFFER_PM, irc_make_buffer_name (s, nickname));
|
||||||
// FIXME: this probably needs to be converted to UTF-8
|
|
||||||
buffer->name = xstrdup_printf ("%s.%s", s->name, nickname);
|
|
||||||
buffer->server = s;
|
buffer->server = s;
|
||||||
buffer->user = user;
|
buffer->user = user;
|
||||||
str_map_set (&s->irc_buffer_map, user->nickname, buffer);
|
str_map_set (&s->irc_buffer_map, user->nickname, buffer);
|
||||||
|
@ -6643,10 +6655,8 @@ irc_handle_join (struct server *s, const struct irc_message *msg)
|
||||||
if (!irc_is_this_us (s, msg->prefix))
|
if (!irc_is_this_us (s, msg->prefix))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
buffer = buffer_new (s->ctx->input);
|
buffer = buffer_new (s->ctx->input,
|
||||||
buffer->type = BUFFER_CHANNEL;
|
BUFFER_CHANNEL, irc_make_buffer_name (s, channel_name));
|
||||||
// FIXME: this probably needs to be converted to UTF-8
|
|
||||||
buffer->name = xstrdup_printf ("%s.%s", s->name, channel_name);
|
|
||||||
buffer->server = s;
|
buffer->server = s;
|
||||||
buffer->channel = channel =
|
buffer->channel = channel =
|
||||||
irc_make_channel (s, xstrdup (channel_name));
|
irc_make_channel (s, xstrdup (channel_name));
|
||||||
|
@ -8316,9 +8326,8 @@ server_add (struct app_context *ctx,
|
||||||
s->config = subtree;
|
s->config = subtree;
|
||||||
|
|
||||||
// Add a buffer and activate it
|
// Add a buffer and activate it
|
||||||
struct buffer *buffer = s->buffer = buffer_new (ctx->input);
|
struct buffer *buffer = s->buffer = buffer_new (ctx->input,
|
||||||
buffer->type = BUFFER_SERVER;
|
BUFFER_SERVER, irc_make_buffer_name (s, NULL));
|
||||||
buffer->name = xstrdup (s->name);
|
|
||||||
buffer->server = s;
|
buffer->server = s;
|
||||||
|
|
||||||
buffer_add (ctx, buffer);
|
buffer_add (ctx, buffer);
|
||||||
|
@ -12358,15 +12367,12 @@ completion_locate (struct completion *self, size_t offset)
|
||||||
self->location = i - 1;
|
self->location = i - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static char *
|
||||||
completion_matches (struct completion *self, int word, const char *pattern)
|
completion_word (struct completion *self, int word)
|
||||||
{
|
{
|
||||||
hard_assert (word >= 0 && word < (int) self->words_len);
|
hard_assert (word >= 0 && word < (int) self->words_len);
|
||||||
char *text = xstrndup (self->line + self->words[word].start,
|
return xstrndup (self->line + self->words[word].start,
|
||||||
self->words[word].end - self->words[word].start);
|
self->words[word].end - self->words[word].start);
|
||||||
bool result = !fnmatch (pattern, text, 0);
|
|
||||||
free (text);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -12461,6 +12467,66 @@ complete_option (struct app_context *ctx, struct completion *data,
|
||||||
strv_free (&options);
|
strv_free (&options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
complete_set_value (struct config_item *item, const char *word,
|
||||||
|
struct strv *output)
|
||||||
|
{
|
||||||
|
struct str serialized = str_make ();
|
||||||
|
config_item_write (item, false, &serialized);
|
||||||
|
if (!strncmp (serialized.str, word, strlen (word)))
|
||||||
|
strv_append_owned (output, str_steal (&serialized));
|
||||||
|
else
|
||||||
|
str_free (&serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
complete_set_value_array (struct config_item *item, const char *word,
|
||||||
|
struct strv *output)
|
||||||
|
{
|
||||||
|
if (!item->schema || item->schema->type != CONFIG_ITEM_STRING_ARRAY)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct strv items = strv_make ();
|
||||||
|
cstr_split (item->value.string.str, ",", false, &items);
|
||||||
|
for (size_t i = 0; i < items.len; i++)
|
||||||
|
{
|
||||||
|
struct str wrapped = str_make (), serialized = str_make ();
|
||||||
|
str_append (&wrapped, items.vector[i]);
|
||||||
|
config_item_write_string (&serialized, &wrapped);
|
||||||
|
str_free (&wrapped);
|
||||||
|
|
||||||
|
if (!strncmp (serialized.str, word, strlen (word)))
|
||||||
|
strv_append_owned (output, str_steal (&serialized));
|
||||||
|
else
|
||||||
|
str_free (&serialized);
|
||||||
|
}
|
||||||
|
strv_free (&items);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
complete_set (struct app_context *ctx, struct completion *data,
|
||||||
|
const char *word, struct strv *output)
|
||||||
|
{
|
||||||
|
if (data->location == 1)
|
||||||
|
{
|
||||||
|
complete_option (ctx, data, word, output);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data->location != 3)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char *key = completion_word (data, 1);
|
||||||
|
struct config_item *item = config_item_get (ctx->config.root, key, NULL);
|
||||||
|
if (item)
|
||||||
|
{
|
||||||
|
char *op = completion_word (data, 2);
|
||||||
|
if (!strcmp (op, "-=")) complete_set_value_array (item, word, output);
|
||||||
|
if (!strcmp (op, "=")) complete_set_value (item, word, output);
|
||||||
|
free (op);
|
||||||
|
}
|
||||||
|
free (key);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
complete_topic (struct app_context *ctx, struct completion *data,
|
complete_topic (struct app_context *ctx, struct completion *data,
|
||||||
const char *word, struct strv *output)
|
const char *word, struct strv *output)
|
||||||
|
@ -12514,33 +12580,30 @@ static char **
|
||||||
complete_word (struct app_context *ctx, struct completion *data,
|
complete_word (struct app_context *ctx, struct completion *data,
|
||||||
const char *word)
|
const char *word)
|
||||||
{
|
{
|
||||||
// First figure out what exactly we need to complete
|
char *initial = completion_word (data, 0);
|
||||||
bool try_commands = false;
|
|
||||||
bool try_options = false;
|
|
||||||
bool try_topic = false;
|
|
||||||
bool try_nicknames = false;
|
|
||||||
|
|
||||||
if (data->location == 0 && completion_matches (data, 0, "/*"))
|
|
||||||
try_commands = true;
|
|
||||||
else if (data->location == 1 && completion_matches (data, 0, "/set"))
|
|
||||||
try_options = true;
|
|
||||||
else if (data->location == 1 && completion_matches (data, 0, "/help"))
|
|
||||||
try_commands = try_options = true;
|
|
||||||
else if (data->location == 1 && completion_matches (data, 0, "/topic"))
|
|
||||||
try_topic = try_nicknames = true;
|
|
||||||
else
|
|
||||||
try_nicknames = true;
|
|
||||||
|
|
||||||
|
// Start with a placeholder for the longest common prefix
|
||||||
struct strv words = strv_make ();
|
struct strv words = strv_make ();
|
||||||
|
|
||||||
// Add placeholder
|
|
||||||
strv_append_owned (&words, NULL);
|
strv_append_owned (&words, NULL);
|
||||||
|
|
||||||
if (try_commands) complete_command (ctx, data, word, &words);
|
if (data->location == 0 && *initial == '/')
|
||||||
if (try_options) complete_option (ctx, data, word, &words);
|
complete_command (ctx, data, word, &words);
|
||||||
if (try_topic) complete_topic (ctx, data, word, &words);
|
else if (data->location >= 1 && !strcmp (initial, "/set"))
|
||||||
if (try_nicknames) complete_nicknames (ctx, data, word, &words);
|
complete_set (ctx, data, word, &words);
|
||||||
|
else if (data->location == 1 && !strcmp (initial, "/help"))
|
||||||
|
{
|
||||||
|
complete_command (ctx, data, word, &words);
|
||||||
|
complete_option (ctx, data, word, &words);
|
||||||
|
}
|
||||||
|
else if (data->location == 1 && !strcmp (initial, "/topic"))
|
||||||
|
{
|
||||||
|
complete_topic (ctx, data, word, &words);
|
||||||
|
complete_nicknames (ctx, data, word, &words);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
complete_nicknames (ctx, data, word, &words);
|
||||||
|
|
||||||
|
cstr_set (&initial, NULL);
|
||||||
LIST_FOR_EACH (struct hook, iter, ctx->completion_hooks)
|
LIST_FOR_EACH (struct hook, iter, ctx->completion_hooks)
|
||||||
{
|
{
|
||||||
struct completion_hook *hook = (struct completion_hook *) iter;
|
struct completion_hook *hook = (struct completion_hook *) iter;
|
||||||
|
|
Loading…
Reference in New Issue