]> Pileus Git - ~andy/lamechat/blobdiff - view.c
Add more completion.
[~andy/lamechat] / view.c
diff --git a/view.c b/view.c
index 9343f0c2116061a6611f1dd7f8097aefce149030..0676e0038fd513516b41019764e3f136d7adfe29 100644 (file)
--- a/view.c
+++ b/view.c
@@ -80,8 +80,8 @@ typedef struct window_t {
 } window_t;
 
 typedef struct {
-       char      *value;
-       char      *desc;
+       const char *value;
+       const char *desc;
 } item_t;
 
 typedef struct {
@@ -129,8 +129,8 @@ static int    deadline;
 static int    theme;
 static int    abbrev;
 static int    draw;
-static char  *watch;
 static int    view;
+static char  *watch;
 
 static char   cmd_buf[4096];
 static int    cmd_pos;
@@ -166,18 +166,18 @@ static short color_date;
 static short color_watch;
 static short color_error;
 
-static const char *themes[] = {
-       [THEME_NORMAL] "normal",
-       [THEME_DARK]   "dark",
-       [THEME_LIGHT]  "light",
+static const char *themes[][2] = {
+       [THEME_NORMAL]    { "normal",   "Default theme"    },
+       [THEME_DARK]      { "dark",     "Dark background"  },
+       [THEME_LIGHT]     { "light",    "Light background" },
 };
 
-static const char *abbrevs[] = {
-       [ABBREV_NONE]     "none",
-       [ABBREV_FIRST]    "first",
-       [ABBREV_LAST]     "last",
-       [ABBREV_FLAST]    "flast",
-       [ABBREV_INITIALS] "initials",
+static const char *abbrevs[][2] = {
+       [ABBREV_NONE]     { "none",     "Do not abbreivate"       },
+       [ABBREV_FIRST]    { "first",    "First name only"         },
+       [ABBREV_LAST]     { "last",     "Last name only"          },
+       [ABBREV_FLAST]    { "flast",    "First initial last name" },
+       [ABBREV_INITIALS] { "initials", "Initials only"           },
 };
 
 /* Helper functions */
@@ -366,7 +366,7 @@ static window_t *prev_window(window_t *win)
        return prev;
 }
 
-static void cycle_channel(void)
+static channel_t *cycle_channel(window_t *win)
 {
        /* Clear seen flags */
        for (int i = 0; i < history; i++)
@@ -379,31 +379,27 @@ static void cycle_channel(void)
                if (msg->channel->seen)
                        continue;
                msg->channel->seen = 1;
-               if (!in_window(msg, focus))
+               if (!in_window(msg, win))
                        continue;
                if (cur)
                        next = msg->channel;
-               if (msg->channel == focus->dest)
+               if (msg->channel == win->dest)
                        cur = msg->channel;
                if (!first)
                        first = msg->channel;
        }
-       focus->dest = next ?: first ?: &sys_chan;
+       return next ?: first ?: &sys_chan;
 }
 
-static void last_channel(void)
+static channel_t *last_channel(window_t *win)
 {
-       if (focus->dest  == &sys_chan &&
-           focus->saved != &sys_chan) {
-               focus->dest = focus->saved;
-               return;
-       }
-       for (int i = history-1; i>=0; i--) {
-               if (in_window(&messages[i], focus)) {
-                       focus->dest = messages[i].channel;
-                       return;
-               }
-       }
+       if (win->dest  == &sys_chan &&
+           win->saved != &sys_chan)
+               return win->saved;
+       for (int i = history-1; i>=0; i--)
+               if (in_window(&messages[i], win))
+                       return messages[i].channel;
+       return win->dest;
 }
 
 static void update_windows(void)
@@ -775,13 +771,15 @@ static int prev_message(int pos)
 }
 
 /* Menu items */
-static void add_item(char *value, char *desc)
+static void add_item(const char *prefix, const char *value, const char *desc)
 {
        item_t item = {
                .value = value,
                .desc  = desc,
        };
        append(&item_buf, (void*)&item, sizeof(item));
+       if (prefix)
+               item_pos = prefix - cmd_buf;
 }
 
 static int num_items(void)
@@ -802,8 +800,8 @@ static int compare_items(const void *_a, const void *_b)
 {
        const item_t *a = _a;
        const item_t *b = _b;
-       return strcmp(a->value, b->value) ?:
-              strcmp(a->desc,  b->desc);
+       return compare(a->value, b->value) ?:
+              compare(a->desc,  b->desc);
 }
 
 static void sort_items(void)
@@ -820,6 +818,40 @@ static void sort_items(void)
 }
 
 /* Commands */
+static void send_complete(const char *text)
+{
+       const char *arg;
+       if (prefix(text, "/theme ", &arg)) {
+               complete_array(arg, themes, N_ELEMENTS(themes));
+       }
+       else if (prefix(text, "/abbrev ", &arg)) {
+               complete_array(arg, abbrevs, N_ELEMENTS(abbrevs));
+       }
+       else if (prefix(text, "/server ", &arg)) {
+               complete_server(arg);
+       }
+       else if (prefix(text, "/channel ", &arg)) {
+               complete_channel(arg);
+       }
+       else {
+               complete_args(text,
+                             "/quit",     "Quit the program",
+                             "/sort",     "Sort chat history",
+                             "/theme ",   "Change the theme",
+                             "/abbrev ",  "Abbreviate names",
+                             "/watch ",   "Watch for regex",
+                             "/open",     "Open window",
+                             "/close",    "Close window",
+                             "/name ",    "Set window name",
+                             "/server ",  "Set window server",
+                             "/channel ", "Set window channel",
+                             "/filter ",  "Set window filter",
+                             NULL);
+       }
+
+       chat_complete(last_channel(focus), text);
+}
+
 static int send_command(const char *text)
 {
        const char *arg;
@@ -833,12 +865,12 @@ static int send_command(const char *text)
        }
        else if (prefix(text, "/theme", &arg)) {
                for (int i = 0; i < N_ELEMENTS(themes); i++)
-                       if (match(themes[i], arg))
+                       if (match(themes[i][0], arg))
                                theme = i;
        }
        else if (prefix(text, "/abbrev", &arg)) {
                for (int i = 0; i < N_ELEMENTS(abbrevs); i++)
-                       if (match(abbrevs[i], arg))
+                       if (match(abbrevs[i][0], arg))
                                abbrev = i;
        }
        else if (prefix(text, "/watch", &arg)) {
@@ -1089,6 +1121,13 @@ static void insert(const char *buf, int len)
        }
 }
 
+static void insert_mb(wint_t chr)
+{
+       char buf[MB_CUR_MAX];
+       int n = wctomb(buf, chr);
+       insert(buf, n);
+}
+
 static void delete(int before, int after)
 {
        if (before > 0 && (cmd_pos-before) >= 0) {
@@ -1107,36 +1146,15 @@ static void delete(int before, int after)
 }
 
 /* Tab completion */
-static void complete_user(const char *prefix)
-{
-       int len = strlen(prefix);
-       for (user_t *cur = users; cur; cur = cur->next) {
-               user_t *alias = cur;
-               while (alias->alias)
-                       alias = alias->alias;
-               if (!strncmp(prefix, cur->name, len) ||
-                   !strncmp(prefix, alias->name, len))
-                       add_item(alias->name, cur->name);
-       }
-}
-
-static void complete_channel(const char *prefix)
-{
-       int len = strlen(prefix);
-       for (channel_t *cur = channels; cur; cur = cur->next)
-               if (!strncmp(prefix, cur->name, len))
-                       add_item(cur->name, cur->topic ?: "No Topic");
-}
-
 static void show_completion(void)
 {
        debug("complete: show");
 
        /* Apply completion */
-       char *pick = get_item(item_idx)->value;
-       char *save = &save_buf[save_pos];
-       cmd_pos  = item_pos;
-       cmd_len  = item_pos;
+       const char *pick = get_item(item_idx)->value;
+       const char *save = &save_buf[save_pos];
+       cmd_pos = item_pos;
+       cmd_len = item_pos;
        insert(pick, strlen(pick));
        insert(save, save_len-save_pos);
        cmd_pos -= save_len-save_pos;
@@ -1146,11 +1164,6 @@ static void start_completion(void)
 {
        debug("complete: start");
 
-       /* Null terminate cmd_buf */
-       static char cmd[sizeof(cmd_buf)];
-       memcpy(cmd, cmd_buf, cmd_pos);
-       cmd[cmd_pos] = '\0';
-
        /* Reset item buffers */
        reset(&item_buf);
        item_idx = 0;
@@ -1158,13 +1171,10 @@ static void start_completion(void)
        item_scroll = 0;
 
        /* Run completion */
-       const char *head = NULL;
-       if (prefix(cmd, "/query", &head))
-               complete_user(head ?: "");
-       if (prefix(cmd, "/join", &head))
-               complete_channel(head ?: "");
-       if (head)
-               item_pos = head - cmd;
+       char tmp = cmd_buf[cmd_pos];
+       cmd_buf[cmd_pos] = '\0';
+       send_complete(cmd_buf);
+       cmd_buf[cmd_pos] = tmp;
 
        /* Sort matches */
        sort_items();
@@ -1209,7 +1219,7 @@ static void input_chat(wint_t chr)
                focus = prev_window(focus) ?: focus;
        }
        else if (chr == KEY_CTRL_X) {
-               cycle_channel();
+               focus->dest = cycle_channel(focus);
        }
        else if (chr == KEY_CTRL_Y) {
                focus->scroll -= 1;
@@ -1240,7 +1250,7 @@ static void input_chat(wint_t chr)
        }
        else if (chr == KEY_TAB) {
                if (cmd_pos == 0)
-                       last_channel();
+                       focus->dest = last_channel(focus);
                else
                        start_completion();
        }
@@ -1276,9 +1286,7 @@ static void input_chat(wint_t chr)
                delete(0, 1);
        }
        else if (iswprint(chr)) {
-               char buf[MB_CUR_MAX];
-               int n = wctomb(buf, chr);
-               insert(buf, n);
+               insert_mb(chr);
        }
 
        /* Unknown control character */
@@ -1290,30 +1298,33 @@ static void input_chat(wint_t chr)
 
 static void input_menu(wint_t chr)
 {
-       if (chr == 'j' ||
+       if (chr == KEY_CTRL_N ||
            chr == KEY_DOWN) {
                if (item_idx+1 < num_items())
                        item_idx++;
                show_completion();
        }
-       else if (chr == 'k' ||
+       else if (chr == KEY_CTRL_P ||
                 chr == KEY_UP) {
                if (item_idx > 0)
                        item_idx--;
                show_completion();
        }
-       else if (chr == KEY_RETURN) {
-               debug("complete: chat");
-               view    = VIEW_CHAT;
+       else if (chr == KEY_TAB) {
+               start_completion();
        }
-       else if (chr == 'q' ||
-                chr == KEY_ESCAPE) {
-               debug("complete: chat");
+       else if (chr == KEY_ESCAPE) {
+               debug("complete: escape");
                memcpy(cmd_buf, save_buf, save_len);
                cmd_pos = save_pos;
                cmd_len = save_len;
                view    = VIEW_CHAT;
        }
+       else {
+               debug("complete: accept");
+               view    = VIEW_CHAT;
+               input_chat(chr);
+       }
 }
 
 /* View init */
@@ -1399,9 +1410,9 @@ void view_config(const char *group, const char *name, const char *key, const cha
 
        if (match(group, "general")) {
                if (match(key, "theme"))
-                       theme = get_map(value, themes);
+                       theme = get_mapv(value, themes);
                else if (match(key, "abbrev"))
-                       abbrev = get_map(value, abbrevs);
+                       abbrev = get_mapv(value, abbrevs);
                else if (match(key, "defocus"))
                        defocus = get_number(value);
                else if (match(key, "watch"))
@@ -1435,7 +1446,7 @@ void view_sync(void)
        while (get_wch(&wch) != ERR) {
                if (view == VIEW_CHAT)
                        input_chat(wch);
-               if (view == VIEW_MENU)
+               else if (view == VIEW_MENU)
                        input_menu(wch);
                deadline = time(NULL) + defocus;
                draw = 1;
@@ -1458,7 +1469,7 @@ void view_sync(void)
                draw_header();
                if (view == VIEW_CHAT)
                        draw_chat();
-               if (view == VIEW_MENU)
+               else if (view == VIEW_MENU)
                        draw_menu();
 
                move(cmd_row, cmd_col);
@@ -1481,3 +1492,59 @@ void view_exit(void)
                return;
        endwin();
 }
+
+/* Completion */
+void complete_item(const char *prefix, const char *value, const char *desc)
+{
+       add_item(prefix, value, desc ?: "");
+}
+
+void complete_user(const char *prefix)
+{
+       for (user_t *cur = users; cur; cur = cur->next) {
+               user_t *alias = cur;
+               while (alias->alias)
+                       alias = alias->alias;
+               if (starts(prefix, cur->name) ||
+                   starts(prefix, alias->name))
+                       add_item(prefix, alias->name, cur->name);
+       }
+}
+
+void complete_channel(const char *prefix)
+{
+       for (channel_t *cur = channels; cur; cur = cur->next)
+               if (starts(prefix, cur->name))
+                       add_item(prefix, cur->name, cur->topic);
+}
+
+void complete_server(const char *prefix)
+{
+       for (server_t *cur = servers; cur; cur = cur->next)
+               if (starts(prefix, cur->name))
+                       add_item(prefix, cur->name, NULL);
+}
+
+void complete_array(const char *prefix, const char *list[][2], int n)
+{
+       for (int i = 0; i < n; i++)
+               if (starts(prefix, list[i][0]))
+                       add_item(prefix, list[i][0], list[i][1]);
+}
+
+void complete_args(const char *prefix, ...)
+{
+       const char *value, *desc;
+       va_list ap;
+       va_start(ap, prefix);
+       while (1) {
+               value = va_arg(ap, const char *);
+               if (!value)
+                       break;
+               desc = va_arg(ap, const char *);
+               if (!starts(prefix, value))
+                       continue;
+               add_item(prefix, value, desc);
+       }
+       va_end(ap);
+}